Not For the Faint of Heart - ADVANCED CONCEPTS
By Stratadrake
Stratadrake
posted on 24-6-2003 at 08:52 PM
Subject: Another little utility for the beta FTP
If you've been
keeping up with Todo's thread in MapMaking forum - you'll have heard me talking
about a little utility I call "Shell WDG". Just a little bit ago I
uploaded it to the FTP, WZCK directory.
Shell WDG is, for the most part, a user interface for calling MakeWDG. However
it does include a secondary function which I call "WDG registration",
it is the function I use to compress all the v1.12 WDG's from their original
2.1MB to their final 900kb in size.
Obviously there are bugs in it but I haven't worried much about them.
The rest of this post is for Kev who asked about how I do the registration
process in code.
To start you need some declarations:
quote:
Type File
' Used to locate the file's data
RecordPtr As Long ' For use during WDG compilation
' As specified by the WDG -- relative to end of WRF section
FileOffset As Long
' VB compatible pointer to this file's data
FilePtr As Long
NewFileOffset As Long ' References to temp file
FileSize As Long
TexPage As Byte
TexFlag As Byte
InterName As Long ' Hash code
Name As String ' Grabbed from MakeWDG.txt
Type As Long ' Type's hash code
TypeName As String ' Grabbed from MakeWDG.txt
End Type
Type FileRef
Name As String ' Type/dir/name
FilePtr As Long
End Type
Type WRF
InterName As Long ' Hash code
Name As String ' From MakeWDG.Txt
Offset As Long ' Position of this WRF in the WDG
DataPtr As Long ' Position of this WRF's data
NumFiles As Long
Files() As File
End Type
Type WDG
InterName As String
ID As Long
PreReq() As ReqWDG
NumWRFs As Integer
WRFs() As WRF
End Type
Private PassThru As Long
Private OneByte As Byte
Private TwoBytes As Integer
Private FourBytes As Long
And now for the registering function (be warned, it's a doozie )
quote:
Public Function RegisterWDG(ByVal FromName As String, ByVal ToName As String) As Integer
PassThru = -1: OneByte = 0: TwoBytes = 0: FourBytes = 0
Dim N As Integer
On Error GoTo SkipReg
Close
Dim AppPath As String, FileName As String
If InStr(FromName, ":") = 0 Then
AppPath = App.Path & IIf(Right$(App.Path, 1) <> "\", "\", "")
FileName = AppPath & FromName
Else
FileName = FromName
End If
If Dir(FileName) = "" Then
Debug.Assert False
Exit Function
End If
If LCase$(Right$(FromName, 4)) <> ".wdg" Then
MsgBox "Please specify the WDG file you wish to compress.", vbInformation
Exit Function
End If
If LCase$(Right$(ToName, 4)) <> ".wdg" Then
MsgBox "Please specify a new file name for the registered WDG.", vbInformation
Exit Function
End If
Dim FolderName As String
FolderName = Left$(FileName, InStrRev(FileName, "\"))
' Grab all the data from MakeWDG.Txt
Dim MakeWDG As Integer
MakeWDG = FreeFile
If Dir(FolderName & "\MakeWDG.txt") = "" Then
MsgBox "Could not locate a MakeWDG verbose output for this WDG file."
Exit Function
End If
Open FolderName & "\MakeWDG.txt" For Binary Access Read As #MakeWDG
Dim DataTXT As String
DataTXT = String(LOF(MakeWDG), 32)
Get #MakeWDG, 1, DataTXT
Close #MakeWDG
Dim WDG As Integer
WDG = FreeFile
Open FileName For Binary Access Read As #WDG
Dim OutFile1 As Integer
OutFile1 = FreeFile
Dim TempFileName As String
TempFileName = App.Path & "\_files.tmp"
If Dir(TempFileName) <> "" Then Kill TempFileName
Open TempFileName For Binary Access Write As #OutFile1
On Error GoTo 0
' Parses through a WDG.
Dim Data As WDG
ReDim Data.PreReq(0)
Dim NullData As WDG
ReDim NullData.PreReq(0)
ReDim NullData.WRFs(0)
' Step 1: Read the WDG header ("WDG4" for Warzone.wdg,
' "WDG5" for everything else)
Dim Sign As String * 4
Get #WDG, 1, Sign$
Dim WDGVer As Integer
WDGVer = Right(Sign$, 1)
' Step 2: Count the WRF's (4 byte integer)
Dim NumWRFs As Long
Get #WDG, 5, NumWRFs&
' Step 3: For ver.5 WDG's, input add-on name/id
If WDGVer% = 5 Then
' Load the internal name for a WDG.
Dim Strin As String * 16
Get #WDG, 9, Strin$
N = InStr(Strin$ & Chr$(0), Chr$(0))
Data.InterName = Left$(Strin$, N - 1)
Get #WDG, &H19, Data.ID
Else
Data.InterName = "warzone"
Data.ID = 0
End If
' Step 4: For rev.5 WDG's, input pre-req WDG's
Dim NumReqs As Long, A As Integer
If WDGVer% = 5 Then
Get #WDG, &H1D, NumReqs&
ReDim Data.PreReq(NumReqs&)
For A = 1 To NumReqs&
Strin$ = String(16, 1)
Get #WDG, , Strin$
N = InStr(Strin$ & Chr$(0), Chr$(0))
Data.PreReq(A).InterName = Left$(Strin$, N - 1)
Get #WDG, , Data.PreReq(A).ID
Next A
Else
ReDim Data.PreReq(0)
End If
Dim NewFiles() As FileRef
ReDim NewFiles(0)
' Parse through each WRF for their file list
ReDim Data.WRFs(NumWRFs&)
Dim WRF As Integer
For WRF = 1 To NumWRFs&
With Data.WRFs(WRF)
' Initialize this WRF and load its data
Dim NextWRF As Long
ReDim .Files(0)
Get #WDG, , .InterName
Get #WDG, , .Offset
Debug.Assert .Offset > 0
Get #WDG, , .NumFiles
ReDim .Files(.NumFiles)
NextWRF = Seek(WDG)
' Record where this WRF's data block begins
.DataPtr = .Offset + 20 * .NumFiles + 1
ReDim .Files(.NumFiles)
' Go to the file table section and load data
Seek #WDG, .Offset + 1
Dim File As Integer
For File = 1 To .NumFiles
With .Files(File)
' 20 bytes read from file section
Get #WDG, , .Type
Get #WDG, , .InterName
Get #WDG, , .FileOffset
Debug.Assert .FileOffset >= -1
' Calculate absolute file position
.FilePtr = Data.WRFs(WRF).DataPtr + .FileOffset
Get #WDG, , .FileSize
Get #WDG, , .TexFlag
Get #WDG, , .TexPage
Get #WDG, , TwoBytes%
Dim LineN As Long, EndL As Long
EndL = 1
Do
LineN = InStr(EndL, DataTXT, vbLf & File - 1 & ") offset=")
EndL = InStr(LineN, DataTXT, vbCr)
If EndL < LineN Then Exit Do
Dim DataLine As String
DataLine = Mid$(DataTXT, LineN + 1, EndL - LineN)
If InStr(1, DataLine, Hex$(.InterName), vbTextCompare) > 0 And _
InStr(1, DataLine, Hex$(.Type), vbTextCompare) > 0 Then
Dim Name1 As Long, Name2 As Long
Dim Dir1 As Long, Dir2 As Long
Dim Type1 As Long, Type2 As Long
Dim ItemName As String, ItemType As String
Name1 = InStr(DataLine, "name=[") + 6
Name2 = InStr(Name1, DataLine, "]")
Dir1 = InStr(DataLine, "dir=[") + 5
Dir2 = InStr(Dir1, DataLine, "]")
.Name = Mid$(DataLine, Dir1, Dir2 - Dir1) & Mid$(DataLine, Name1, Name2 - Name1)
Type1 = InStr(DataLine, "type=[") + 6
Type2 = InStr(Type1, DataLine, "]")
.TypeName = Mid$(DataLine, Type1, Type2 - Type1)
ItemName = .TypeName & " | " & .Name
' Check if the file is already contained in the WDG
Dim Item As Integer
For Item = 1 To UBound(NewFiles)
If ItemName = NewFiles(Item).Name Then
Exit For
End If
Next Item
Dim FileData As String, Byt As Byte
' If this is a new file, make space for it.
If Item > UBound(NewFiles) Then
ReDim Preserve NewFiles(Item)
NewFiles(Item).Name = ItemName
Dim ThisPos As Long
ThisPos& = Seek(WDG)
If .FileSize > 0 Then
FileData = String$(.FileSize, 32)
Dim FileOffset As Long
FileOffset = Data.WRFs(WRF).DataPtr + .FileOffset
Get #WDG, FileOffset, FileData
Seek #WDG, ThisPos&
' Encode type name
PutStr OutFile1, .TypeName
' Encode file dir/name
PutStr OutFile1, .Name
' Grab the file pointer NOW
NewFiles(Item).FilePtr = Seek(OutFile1) - 1
' Output the file's data with an ASCII zero at the end
Put #OutFile1, , FileData
Byt = 0: Put #OutFile1, , Byt
Debug.Print Hex$(NewFiles(Item).FilePtr), .TypeName, .Name
End If
Else
End If
Exit Do
End If
Loop
End With
Next File
' Return to the WRF list
Seek #WDG, NextWRF&
End With
Next WRF
Close #WDG
' Now, begin WRITING the new WDG!
Dim OutWDG As Integer
OutWDG = FreeFile
'If InStr(ToName, ":") = 0 Then ToName = App.Path & "\" & ToName
If Dir(FolderName & ToName) <> "" Then Kill FolderName & ToName
Open FolderName & ToName For Binary Access Read Write As #OutWDG
' 1 - WDG file header
Dim Stri As String * 4
Stri = "WDG5"
Put #OutWDG, 1, Stri
' 2 - Output # of WRF's
NumWRFs& = UBound(Data.WRFs)
Put #OutWDG, 5, NumWRFs&
' 3 - Output internal ID of this WDG.
Dim ReqWDG As String * 16
ReqWDG = Data.InterName & String$(16, 0)
Put #OutWDG, 9, ReqWDG ' 16 bytes
Put #OutWDG, &H19, Data.ID ' 4 bytes
' 4 - Output co-requisite WDG's.
NumReqs& = UBound(Data.PreReq)
Put #OutWDG, &H1D, NumReqs&
For A = 1 To NumReqs&
With Data.PreReq(A)
ReqWDG = .InterName & String$(16, 0)
Put #OutWDG, , ReqWDG ' Padded to 16 bytes
Put #OutWDG, , .ID ' 4 bytes
End With
Next A
' 5 - Make space for WRF pointers -- fill in later!
Dim Ptrs() As Long
ReDim Ptrs(UBound(Data.WRFs))
For A = 1 To UBound(Data.WRFs)
Put #OutWDG, , Data.WRFs(A).InterName
' Offset
Ptrs(A) = Seek(OutWDG)
Put #OutWDG, , FourBytes
' Output # of files
Put #WDG, , Data.WRFs(A).NumFiles
Next A
' 6 - Output all the WRF datas together
For A = 1 To UBound(Data.WRFs)
With Data.WRFs(A)
' Encode the WRF name
PutStr OutWDG, .Name
' Record the byte offset (file location -1)
Dim PosWRF As Long
PosWRF = Seek(OutWDG) - 1
' Output WRF pointer in C compatible format
Debug.Assert PosWRF >= 0
Put #OutWDG, Ptrs(A), PosWRF
Seek #OutWDG, (PosWRF + 1)
Dim B As Integer
For B = 1 To UBound(Data.WRFs(A).Files)
With .Files(B)
Put #OutWDG, , .Type ' 4 bytes
Put #OutWDG, , .InterName ' 4 bytes
If .FileSize > 0 Then
' Note file position for later use
.RecordPtr = Seek(OutWDG)
Put #OutWDG, , FourBytes
Else
' Passthru file.
.RecordPtr = -1
Put #OutWDG, , PassThru
End If
Put #OutWDG, , .FileSize ' 4 bytes
Put #OutWDG, , .TexFlag ' 1 byte
Put #OutWDG, , .TexPage ' 1 byte
Put #OutWDG, , TwoBytes
End With
Next B
.DataPtr = Seek(OutWDG) ' Start of data section -- important!
End With
Next A
' Grab the pointer for end of all WRF datas.
Dim LastPtr As Long
LastPtr = Seek(OutWDG)
' Fill in the missing file pointers
For A = 1 To UBound(Data.WRFs)
For B = 1 To UBound(Data.WRFs(A).Files)
With Data.WRFs(A).Files(B)
If .FileSize > 0 Then
' Locate the file we were adding
Dim I As Integer
For I = 1 To UBound(NewFiles)
If NewFiles(I).Name = .TypeName & " | " & .Name Then Exit For
Next I
Debug.Assert I <= UBound(NewFiles)
' Create WZ-compatible file offset value
' (relative to WRF DataPtr i.e. end of WRF data)
FileOffset = (LastPtr - Data.WRFs(A).DataPtr) + NewFiles(I).FilePtr '- Data.WRFs(A).DataPtr
Debug.Assert FileOffset >= -1
' Take that pointer and shove it into place
Put #OutWDG, .RecordPtr, FileOffset
End If
End With
Next B
Next A
' Copy the tempfile data into the final (compacted) WDG.
OutFile1 = FreeFile
Open TempFileName For Binary Access Read As #OutFile1
FileData = String$(LOF(OutFile1), 32)
Get #OutFile1, 1, FileData
Close #OutFile1
Put #OutWDG, LastPtr, FileData
Close #OutWDG
Debug.Print " -- Done! --"
Close
Exit Function
SkipReg:
RegisterWDG = -Err.Number
Debug.Assert False
' These 2 statements for debug purposes
'On Error GoTo 0
'Resume
Close
End Function
Impulse posted on 24-6-2003 at 08:56 PM
Strat, I have a newfound respect for you after reading that.
Shibby(tm)
Stratadrake posted on 24-6-2003 at 09:05 PM
Yeah, how else can you explain v1.12 having 5 custom/modified texpages, over two dozen custom/modified PIE files, and it's still smaller than the v1.11 WDG by 300kb ?
Willis posted on 24-6-2003 at 10:28 PM
ya know... i started reading... then i glanced at the lengh of my scroll bar... started randomly scrolling downward... n all the way i was goin *lalalalalalalalala - i dont see this - lalalalala*
Strata I must admit... DAMN IM GLAD IM NOT YOU! lol... id prob go nuts havin to know what all that means.
But to my main reason of this post... knowing how everything beta related sooner or later should make the public... (IMO at least lol)... how would the average map maker USE this..thing?
Stratadrake posted on 25-6-2003 at 03:24 AM
Hmm, I suppose it's less for the "average" modmaker and more for the slight "above average" modmakers . . . meaning those that need to compile their mods by hand using MakeWDG itself . . . .
Oh there is one thing I didn't mention: You can also run it from the command line.
Kevin posted on 25-6-2003 at 03:42 PM
Ok, so it needs a source wdg, and the verbose output from makewdg?
code: "PreReq() As ReqWDG"
error: "User defined type not defined"
I think you forgot a few declarations?
what is "_files.tmp" used for?
quote:I hope to change that a little, but I guess it will always be harder then wzck.
Hmm, I suppose it's less for the "average" modmaker and more for the slight "above average" modmakers . . . meaning those that need to compile their mods by hand using MakeWDG itself . . . .
quote:
ya know... i started reading... then i glanced at the lengh of my scroll bar... started randomly scrolling downward... n all the way i was goin *lalalalalalalalala - i dont see this - lalalalala*
quote:it would be build into other programs like wzck and a custom gui makewdg program I plan to make, so the resulting wdg files are smaller
But to my main reason of this post... knowing how everything beta related sooner or later should make the public... (IMO at least lol)... how would the average map maker USE this..thing?
[Edited on 25-6-2003 by Kevin]
Stratadrake posted on 25-6-2003 at 05:52 PM
Silly me, the "reqWDG" type was in a different module:
quote:
Public Type ReqWDG
InterName As String
ID As Long
End Type
You will also need:
quote:
Public Sub PutStr(ByVal File As Integer, ByRef Data As String)
Dim Byt As Byte
Put #File, , Data
Byt = Len(Data): Put #File, , Byt
End Sub
There ya go. (Note that the PutStr function stores the length descriptor byte (Byt) after the actual string -- this is important)
During the registration/compression process, "_files.tmp" is a stash file that Shell WDG assembles. This eventually becomes the single data section of the registered WDG:
quote:
Dim TempFileName As String
TempFileName = App.Path & "\_files.tmp"
If Dir(TempFileName) <> "" Then Kill TempFileName
Open TempFileName For Binary Access Write As #OutFile1
. . . Then . . .
quote:
' Check if the file is already contained in the WDG
Dim Item As Integer
For Item = 1 To UBound(NewFiles)
If ItemName = NewFiles(Item).Name Then
Exit For
End If
Next Item
Dim FileData As String, Byt As Byte
' If this is a new file, make space for it.
If Item > UBound(NewFiles) Then
ReDim Preserve NewFiles(Item)
NewFiles(Item).Name = ItemName
Dim ThisPos As Long
ThisPos& = Seek(WDG)
If .FileSize > 0 Then
FileData = String$(.FileSize, 32)
Dim FileOffset As Long
FileOffset = Data.WRFs(WRF).DataPtr + FileOffset
Get #WDG, FileOffset, FileData
Seek #WDG, ThisPos&
' Encode type name
PutStr OutFile1, .TypeName
' Encode file dir/name
PutStr OutFile1, .Name
' Grab the file pointer NOW
NewFiles(Item).FilePtr = Seek(OutFile1) - 1
' Output the file's data with an ASCII zero at the end
Put #OutFile1, , FileData
Byt = 0: Put #OutFile1, , Byt
Debug.Print Hex$(NewFiles(Item).FilePtr), TypeName, .Name
End If
Else
End If
. . . And finally . . .
quote:
' Copy the tempfile data into the final (compacted) WDG.
OutFile1 = FreeFile
Open TempFileName For Binary Access Read As #OutFile1
FileData = String$(LOF(OutFile1), 32)
Get #OutFile1, 1, FileData
Close #OutFile1
Put #OutWDG, LastPtr, FileData
Close #OutWDG
* * * * *
quote:
and a custom gui makewdg program I plan to make, so the resulting wdg files are smaller
You won't be able to get them much smaller than I can manage via Shell WDG because the WDG architecture won't allow it.
As for a program to finally put makeWDG to rest . . . all we need is that algorithm
One note about the PutStr function -- normally when I output strings to file I put a length descriptor byte first, not last. Reason why Shell WDG puts said byte after the actual string is so that it can be read backwards. You see, when you compile a WDG via MakeWDG, the only thing I can rely on is the file pointer. Since the file data reads forwards from that pointer, any important non-WZ data must read backwards from that pointer, so a length descriptor byte, naturally, comes first -- last -- whatever
When you compile a WDG through MakeWDG, the two bytes preceding each actual file's data are zeros since all other sections in the WDG have zeros at their end. So if we make a WDG Explorer 2.0 or something with filenames capability, if there is a zero in front of each file data, then the file has no name specified (length = 0), whereas if it is nonzero, the reader program shall rewind a little and read in the file's original name (as specified by the WRF). Nifty
[Edited on 6-26-2003 by Stratadrake]Kevin posted on 25-6-2003 at 06:03 PM
Thanks.
I am almost done with the create wdg program, as mentioned in the other topic (except for the compression, which I have to uncomment)
since it's already here, you can play with it until I put in the stuff you just posted
This link is outdated, I put the new one over it anyway
put it in a mod folder (with makewdg etc), run it (with autoload ckecked), close it, note changes, try running it again
[Edited on 26-6-2003 by Kevin]
Stratadrake posted on 25-6-2003 at 06:16 PM
Curious, the design looks mighty familiar
I am also wondering, how does your program record the MakeWDG verbose output?
This is also weird, with the Autoload checked, the gui starts up once, and then on each successive startup, it alternatingly does and does not run MakeWDG . . . . hmmm.....
[Edited on 6-26-2003 by Stratadrake]
Kevin posted on 25-6-2003 at 06:42 PM
quote:I had a certain other program to look at for reference
Curious, the design looks mighty familiar
quote:I had a problem with that at first. seems the shell command won't work with ">". my solution was to create a temporary batch file with the same command line and run that, then remove the batch file (may be disabled for debuging, don't know)
I am also wondering, how does your program record the MakeWDG verbose output?
quote:Explain this more
This is also weird, with the Autoload checked, the gui starts up once, and then on each successive startup, it alternatingly does and does not run MakeWDG . . . . hmmm.....
The program as intended (and as it works on this computer) creates a file called "AutoLoad.txt" if you select the check box. when the program starts up, if it can find that file, it simply does what it did last time without showing the gui. This is the main feature and purpose of the program, as you only have to double click to re-compile (and compress) a wdg file
--------------
error detected in registration code
code:
Dim FolderName As String FolderName = Left$(FileName, InStrRev(FileName, ""))
this code does not work. replacing "" with "\"
[Edited on 26-6-2003 by Kevin]
Kevin posted on 25-6-2003 at 08:12 PM
version 1.1 is now available. this version features the ability to compress your wdg files using an algorithm created by Stratadrake. there is also a debug button for the registration that I don't think needs removing.
other than that, it works the same as last time.
I have a problem with the fact that the shell command is not modal. currently it just pauses for 3 seconds before compressing.
Create WDG Version 1.1 - see caution below...c25
coyote25 posted on 25-6-2003 at 10:06 PM
is it possible to add this to the tutorials - for those that are doing the modding stuff by hand...or is there more to come... i am requesting due to the thread - and everyone not having access to this particular part.
Kevin posted on 25-6-2003 at 10:47 PM
I have no objection to this being made available for public use. Keep in mind that this was only started and finished today, so there may still be a few bugs and such.
I designed this program for people like me, who otherwise would have to make batch files for every mod. It is basically a gui interface for makewdg, with the added features of quick re-compiling and compression.
If you want any features added to it, now's the time to ask.
[Edited on 26-6-2003 by Kevin]
Stratadrake posted on 26-6-2003 at 03:02 AM
Okay, initial run, set up options, hit compile. All goes without a hitch.
2nd run. GuiWDG says "bad command or file name" when running the batch file.
3rd run. No problems, MakeWDG works just fine.
4th run. Same as 2nd run.
5th run. Same as 3rd run.
(etc.)
It's a really interesting sort of bug....
The reason why I asked about how you capture the MakeWDG verbose output is because, when I run yours and it calls MakeWDG, it outputs both to the screen and to the text file. When I run mine, it outputs only to the text file (not to the screen).
Another tip: Here's how you can keep GuiWDG from trying to register/compress a WDG before it is fully written. MakeWDG does all its stuff in a tmp file (wdg.tmp to be exact), and only when it has finished does it actually switch it to the actual WDG filename . . . a simple Do: DoEvents: Loop Until FileLen(name) > 0 construct will work just fine
In fact, this tempfile business is good stuff and used a lot in commercial programs -- that way, for example, whenever you save over an existing file, if an error occurs while saving it, you won't end up with a half-saved (and most likely corrupted) file on disk.
Kevin posted on 26-6-2003 at 10:07 AM
quote:That's the strangest bug i've seen, made stranger by it not happening on my computer.
Okay, initial run, set up options, hit compile. All goes without a hitch.
2nd run. GuiWDG says "bad command or file name" when running the batch file.
3rd run. No problems, MakeWDG works just fine.
4th run. Same as 2nd run.
5th run. Same as 3rd run.
I do have a hunch what it might be, but It shouldn't ever happen. stupid windows.
quote:
Another tip: Here's how you can keep GuiWDG from trying to register/compress a WDG before it is fully written. MakeWDG does all its stuff in a tmp file (wdg.tmp to be exact), and only when it has finished does it actually switch it to the actual WDG filename . . . a simple Do: DoEvents: Loop Until FileLen(name) > 0 construct will work just fine
I thought about that, and will try it, but what happens if the temp file doesn't get written? your program will hang until it does. I'll work on it
quote:actually, since I use the ">" to send it to a text file, it shouldn't. this is probably due to the way makewdg outputs to the screen more than anything
The reason why I asked about how you capture the MakeWDG verbose output is because, when I run yours and it calls MakeWDG, it outputs both to the screen and to the text file. When I run mine, it outputs only to the text file (not to the screen).
[Edited on 26-6-2003 by Kevin]
Kevin posted on 26-6-2003 at 11:44 AM
Version 1.1.1 is now on the beta ftp (the ftp for that web host is being annoying) I put it in a 'Utilities' folder because nothing else fit
changes made:
Made sure working directory is set to application path when compiling
removed any relative file accesses I found
added in a check for when the compiling is done (it should wait up to 10 seconds)
I have some other things to try though, and there may be a second revision soon
I have uploaded the second revision. There are not many large changes from the first, but there are a few commands make my life a lot easier, and a few error checking things
[Edited on 26-6-2003 by Kevin]
===================================================================================
coyote25 posted on 26-6-2003 at 04:23 PM
any chance this will make it into the utilities section of the p2 main site?? once all the bugs are ironed out??
Kevin posted on 26-6-2003 at 07:00 PM
I think it could, but it's up to the webmaster to do that kind of update...
Kevin posted on 27-6-2003 at 09:18 PM
And for those specific people who can't get the beta ftp
Attachment: wdgGui.exe (60kb)