IonicWind Software

IWBasic => General Questions => Topic started by: Brian on September 05, 2018, 05:28:49 AM

Title: DLL creation
Post by: Brian on September 05, 2018, 05:28:49 AM
Hi,

I wonder if anyone knows how to create a DLL containing multiple images? I have just had a run-through with the sample DLL code, and it worked OK, but I don't know how to add images, and then retrieve them for use in an IWB program. Any ideas, anyone?

Brian
Title: Re: DLL creation
Post by: Andy on September 05, 2018, 08:35:35 AM
Brian,

Can you not have a resource file and add the images into it.

Then you could add / use LarryMc's "ExtractResource" in the dll?

https://www.ionicwind.com/forums/index.php?topic=5614.msg41457#msg41457

Maybe?

Andy.


Title: Re: DLL creation
Post by: LarryMc on September 05, 2018, 09:11:41 AM
OK, you say you can create a DLL.
set it up as a project with a .rc file
in the .rc file make an entry for each image that looks like this
201 BITMAP "\\img\\0001.bmp"
where 201 is a unique id number and
"\\img\\0001.bmp" is the name and path to the image.

export load_bitmap
Const IMAGE_BITMAP = 0
Declare import, LoadImageA(hinst:int, lpszName:pointer, uType:int, cxDesired:int, cyDesired:int, fuLoad:int),uint

sub load_bitmap(hint:int,imgno:int,w:int,h:int),uint
string bname
uint hBitmap2
hBitmap2=0
bname="#"+ltrim$(str$(imgno+200))
hBitmap2 = LoadImageA(hInt, bname, IMAGE_BITMAP, w,h, 0)
return hBitmap2
endsub

The above code should also be part of your dll.
This little adjustment is so that you can start calling img #1 from your program with this number scheme.

====================================
as usual your dll would go in the same folder as your app
the following line goes at the top of your app
DECLARE "Shape2LM.dll",load_bitmap(hint:int,imgno:int,w:int,h:int),uint

then load the bitmap with a call
uint hBitmap2=load_bitmap(hint,1,w,h)

and that's the basics
Title: Re: DLL creation
Post by: Brian on September 05, 2018, 11:53:44 AM
Larry,

Excellent - that's just what I needed. Right, here we go!

Brian
Title: Re: DLL creation
Post by: Brian on September 06, 2018, 06:22:38 AM
Larry, I keep getting this. Followed your instructions implicitly

Compiling...
a window frame.iwb
No Errors

Linking...
IWBasic Linker v1.11 Copyright © 2011 Ionic Wind Software
Unresolved external __imp_load_bitmap
Error: C:\Users\Public\Documents\IWBasic\projects\Hairdressing\a window frame.o - Unresolved extern __imp_load_bitmap
Error(s) in linking C:\Users\Public\Documents\IWBasic\projects\Hairdressing\a window frame.exe

Brian
Title: Re: DLL creation
Post by: LarryMc on September 06, 2018, 08:09:04 AM
you do know that in order to compile any app that uses a call to any dll you have to create a linking library.

I assumed you did

From the main menu of the IDE select Tools/Create Import Library
When the dialog opens find the dll you created and open it.
It will create a .lib with the same name as your dll and put it in your libs folder.

Now try to compile your app.
Title: Re: DLL creation
Post by: Brian on September 06, 2018, 11:31:51 AM
Larry,

I confess I hadn't done that. It now compiles fine, but doesn't show anything. I really want my image values to start at 1000. My .rc lines start at "1000 BITMAP" etc. Don't see why it shouldn't work with those values

Brian
Title: Re: DLL creation
Post by: LarryMc on September 06, 2018, 01:01:05 PM
without seeing your actual code and how you are using the handle to the bitmap I can't offer and further suggestion as to why it isn't working for you.
as for the numbering, you're correct; 200 or 1000 it doesn't matter.

are you actually getting a handle returned when you call load_bitmap?
Title: Re: DLL creation
Post by: Brian on September 06, 2018, 01:32:55 PM
No. This line: UINT hBitmap2=load_bitmap(hint,1,50,50) returns a zero

Brian

These are my files that  am compiling. Had to rename the .rc with an extension of .txt
Title: Re: DLL creation
Post by: LarryMc on September 06, 2018, 03:30:43 PM
I'm not seeing why it's not working.

This code came from my custom button library.

question: why did you want your images in a separate dll instead of in your app itself?  Just curious.

Title: Re: DLL creation
Post by: LarryMc on September 06, 2018, 03:45:41 PM
also, instead of putting that long full path name in your rc file.
just make a sub folder off your source file folder called img
and then type your rc entries like this
Quote1000 BITMAP "img\\arrow.bmp"
1001 BITMAP "img\\button.bmp"
1002 BITMAP "img\\custom.bmp"
1003 BITMAP "img\\help.bmp"
1004 BITMAP "img\\inc.bmp"
1005 BITMAP "img\\incx.bmp"
1006 BITMAP "img\\zoomin.bmp"
1007 BITMAP "img\\zoominx.bmp"
1008 BITMAP "img\\zoomout.bmp"
1009 BITMAP "img\\zoomoutx.bmp"
1010 BITMAP "img\\ICexclamation.bmp"
1011 BITMAP "img\\ICinfo.bmp"
1012 BITMAP "img\\ICquestion.bmp"
1013 BITMAP "img\\ICstop.bmp"

Title: Re: DLL creation
Post by: fasecero on September 06, 2018, 09:11:28 PM
QuoteThis line: UINT hBitmap2=load_bitmap(hint,1,50,50) returns a zero

Which hinstance are you using? If the resource is hosted in the dll you have to use the dll's hinstance.
Title: Re: DLL creation
Post by: LarryMc on September 06, 2018, 09:19:42 PM
getting the hint value is the line of code that is missing
Title: Re: DLL creation
Post by: Brian on September 07, 2018, 02:47:07 AM
Larry and Fasecero,

Thanks for your replies. I normally do embed my graphics into the exe, but there are so many in this program. I'm up to 500kb so far, and still more to add. And I am going to shorten the path name down when I have it figured out. It's certainly a good learning exercise, anyway!

Although, how do I get the hInt value?

Brian
Title: Re: DLL creation
Post by: fasecero on September 07, 2018, 07:45:27 AM
DECLARE IMPORT, GetModuleHandle ALIAS GetModuleHandleA(lpModuleName AS POINTER),INT

sub load_bitmap(imgno:int,w:int,h:int),uint
string bname
uint hBitmap2
hBitmap2=0
bname="#"+ltrim$(str$(imgno+200))
hBitmap2 = LoadImageA(GetModuleHandle(0), bname, IMAGE_BITMAP, w,h, 0)
return hBitmap2
endsub


Didn't tested the code but it should work. I remember myself a long time ago asking this same question and I think Larry told me that there was a global variable holding this value but I couldn't find that post.
Title: Re: DLL creation
Post by: Brian on September 07, 2018, 09:02:21 AM
Nope - recompiled, made new lib file. Still hBitmap2 returning zero, and no image shown

Might just give up on it, but I would really know how to do it!

Brian
Title: Re: DLL creation
Post by: fasecero on September 07, 2018, 01:00:46 PM
This was a tough one. GetModuleHandle() return the executable hinstance even when it is called from the dll, didn't know that. The code below assumes that there is a 1001 bitmap resource

Dll

EXPORT load_bitmap
CONST IMAGE_BITMAP = 0
Declare import, LoadImageA(hint:int, lpszName:pointer, uType:int, cxDesired:int, cyDesired:int, fuLoad:int),uint
DECLARE IMPORT, GetModuleHandle ALIAS GetModuleHandleA(lpModuleName AS POINTER),INT
DECLARE IMPORT, _GetLastError ALIAS GetLastError(),INT

SUB MAKEINTRESOURCE(n as WORD),POINTER
pointer pReturn = 0
_asm
lea eax,[ebp-4]
mov ebx,[ebp+8]
mov [eax],ebx
_endasm
RETURN pReturn
endsub

EXTERN _hinstance as uint

sub load_bitmap(imgno:int,w:int,h:int),uint
RETURN LoadImageA(_hinstance, MAKEINTRESOURCE(imgno), IMAGE_BITMAP, w,h, 0)
endsub


Test

$include "windowssdk.inc"

$use "cfImages.lib"
DECLARE "cfImages.dll",load_bitmap(imgno:int,w:int,h:int),uint

WINDOW win
INT hInt
UINT hBitmap2

OPENWINDOW win,0,0,800,600,@MINBOX|@MAXBOX|@SIZE|@NOAUTODRAW,0,"Window",&win_handler
hBitmap2=load_bitmap(1001,50,50)
setcaption win,str$(hBitmap2)
SHOWIMAGE win,hBitmap2,@IMGBITMAP,50,50,50,50

WAITUNTIL ISWINDOWCLOSED(win)
END

SUB win_handler(),INT
SELECT @MESSAGE
CASE @IDCREATE
CENTERWINDOW win
CASE @IDCLOSEWINDOW
CLOSEWINDOW win
ENDSELECT
RETURN 0
ENDSUB
Title: Re: DLL creation
Post by: fasecero on September 07, 2018, 05:29:13 PM
After several changes I didn't realize that I posted an alternate DLL code. For simplicity, here's Larry's original dll with the only change required being changing the hinst


EXPORT load_bitmap
CONST IMAGE_BITMAP = 0
Declare import, LoadImageA(hint:int, lpszName:pointer, uType:int, cxDesired:int, cyDesired:int, fuLoad:int),uint
DECLARE IMPORT, GetModuleHandle ALIAS GetModuleHandleA(lpModuleName AS POINTER),INT

EXTERN _hinstance as uint

sub load_bitmap(imgno:int,w:int,h:int),uint
string bname
uint hBitmap2
hBitmap2=0
bname="#"+ltrim$(str$(imgno))
hBitmap2 = LoadImageA(_hinstance, bname, IMAGE_BITMAP, w,h, 0)
return hBitmap2
endsub
Title: Re: DLL creation
Post by: Brian on September 08, 2018, 02:15:48 AM
Fasecero,

So which one do I use? And why do we need the hash symbol? Will be trying later today

Brian

Edit: Fasecero, your first solution works! Thank you
Title: Re: DLL creation
Post by: fasecero on September 08, 2018, 07:31:51 AM
Yeah, sorry for the confusion. You can use either of the two.
Title: Re: DLL creation
Post by: Brian on September 20, 2018, 06:56:40 AM
Fasecero,

Sorry to bother you again, but I have been messing trying to make a bitmap button from your code, but with no success so far

For simplicity, I've been using Load_PNG_FromFile. Any ideas?

Brian
Title: Re: DLL creation
Post by: fasecero on September 20, 2018, 10:31:59 AM
I believe that SETBUTTONBITMAPS should work just fine
SETBUTTONBITMAPS w1,BUTTON_1,Load_PNG_FromFile(...),0,0

Otherwise can you upload an example so we can see what happens?
Title: Re: DLL creation
Post by: Brian on September 20, 2018, 12:01:41 PM
Well, as you can see, I didn't get very far

I can display the image, but as for making it a button . . .

Brian
Title: Re: DLL creation
Post by: fasecero on September 20, 2018, 05:28:56 PM
Here are two different ways to create a button with an image. Unfortunately RGNFROMBITMAP requires a .bmp from file or resource (too bad it doesn't accept an HBITMAP), so I can only show you how to make square buttons.


$include "windowssdk.inc"
$include "cfImages.inc"

WINDOW win
UINT hCake

OPENWINDOW win,0,0,640,480,@MINBOX|@MAXBOX|@SIZE,0,"PNG as button",&win_handler
SETWINDOWCOLOR win,RGB(200,255,200)

hCake=Load_PNG_FromFile(GETSTARTPATH+"birthday cake.png",100,100,0xFFC8FFC8)
SHOWIMAGE win,hCake,@IMGBITMAP,270,50,100,100

' method #1
CONST BUTTON_1 = 1
CONTROL win,@RGNBUTTON,"",105,201,104,104,0x50000000,BUTTON_1
SETBUTTONBITMAPS win, BUTTON_1, hCake, hCake, hCake

' method #2
CONST BUTTON_2 = 2
CONTROL win,@SYSBUTTON,"",265,201,104,104,0x50000000,BUTTON_2
ButtonSetImage(win, BUTTON_2, hCake)

WAITUNTIL ISWINDOWCLOSED(win)
DELETEIMAGE hCake,@IMGBITMAP
END

SUB win_handler(),INT
SELECT @MESSAGE
CASE @IDCREATE
CENTERWINDOW win
CASE @IDCLOSEWINDOW
CLOSEWINDOW win
CASE @IDCONTROL
SELECT @CONTROLID
CASE BUTTON_1
CASE& BUTTON_2
IF @NOTIFYCODE = 0
/*button clicked*/
SETCAPTION(win,"You have just clicked one button")
ENDIF
ENDSELECT
ENDSELECT
RETURN 0
ENDSUB

SUB ButtonSetImage(WINDOW w, INT id, INT image)
INT handle = GetDlgItem(w.hwnd, id)
LONG style = GetWindowLong(handle,GWL_STYLE)
style = style | BS_BITMAP
SetWindowLong(handle,GWL_STYLE,style)
_SendMessage(handle, BM_SETIMAGE, IMAGE_BITMAP, image)
_SendMessage(handle, WM_CHANGEUISTATE, MAKELONG(UIS_SET, UISF_HIDEFOCUS), 0)
ENDSUB
Title: Re: DLL creation
Post by: Brian on September 21, 2018, 04:36:50 AM
Fasecero,

That set me looking for a way to remove the border, and this was the best I could do. There is no border on the bottom right button, and only shows a frame when you press it. The frame disappears when you press another button. Many thanks,

Brian
Title: Re: DLL creation
Post by: LarryMc on September 21, 2018, 05:05:52 AM
missing $include "cfImages.inc"
Title: Re: DLL creation
Post by: Brian on September 21, 2018, 07:03:13 AM
Larry,

It's included on a previous post under this topic in zip file birthday.zip (186.73 kB)

Brian
Title: Re: DLL creation
Post by: fasecero on September 21, 2018, 07:53:35 AM
If you do not want any edge use a static. The subclass is optional to change the cursor when the mouse is over the control.


$include "windowssdk.inc"
$INCLUDE "Commctrl.inc"
$include "cfImages.inc"

WINDOW win
UINT hCake
INT subclassID = 12345

OPENWINDOW win,0,0,640,480,@MINBOX|@MAXBOX|@SIZE,0,"PNG as button",&win_handler
SETWINDOWCOLOR win,RGB(200,255,200)

hCake=Load_PNG_FromFile(GETSTARTPATH+"birthday cake.png",100,100,0xFFC8FFC8)
SHOWIMAGE win,hCake,@IMGBITMAP,270,50,100,100

CONST STATIC_1=1
CONTROL win,@STATIC,"",265,201,104,104,0x50000000,STATIC_1
StaticSetImage(win,STATIC_1,hCake)
SetWindowSubclass(GetDlgItem(win.hwnd, STATIC_1), &subclassStaticProc, subclassID, 0)

WAITUNTIL ISWINDOWCLOSED(win)
DELETEIMAGE hCake,@IMGBITMAP
RemoveWindowSubclass(GetDlgItem(win.hwnd, STATIC_1), &subclassStaticProc, subclassID)
END

SUB win_handler(),INT
SELECT @MESSAGE
CASE @IDCREATE
CENTERWINDOW win
CASE @IDCLOSEWINDOW
CLOSEWINDOW win
CASE @IDCONTROL
SELECT @CONTROLID
CASE STATIC_1
IF @NOTIFYCODE = 0
SETCAPTION(win,"You have just clicked the static")
ENDIF
ENDSELECT
ENDSELECT
RETURN 0
ENDSUB

SUB subclassStaticProc(hWnd:INT,uMsg:INT,wParam:INT,lParam:INT,uIdSubclass:UINT_PTR,dwRefData:DWORD_PTR),INT
SELECT uMsg
CASE WM_SETCURSOR
WINDOW w
w.hwnd = hWnd
SETCURSOR(w, @CSCUSTOM, LoadCursor(NULL, IDC_HAND))
RETURN TRUE
ENDSELECT

RETURN DefSubclassProc(hWnd, uMsg, wParam, lParam)
ENDSUB

SUB StaticSetImage(WINDOW w,INT id,INT image)
INT handle=GetDlgItem(w.hWnd,id)
LONG style=GetWindowLong(handle,GWL_STYLE)
style=style|SS_BITMAP|SS_NOTIFY
SetWindowLong(handle,GWL_STYLE,style)
_SendMessage(handle,STM_SETIMAGE,IMAGE_BITMAP,image)
ENDSUB

Title: Re: DLL creation
Post by: Brian on September 21, 2018, 08:19:07 AM
Well, we are learning some stuff at the moment! Works well here, as does all your code

Many thanks,

Brian
Title: Re: DLL creation
Post by: fasecero on September 21, 2018, 11:28:01 AM
Also, if we want to complicate things even more :) we can make an OWNERDRAW button that uses a different image for each state: normal, over, pressed.


$include "windowssdk.inc"
$INCLUDE "Commctrl.inc"
$include "cfImages.inc"

WINDOW win
UINT hCake,hCakeOver,hCakePressed
INT subclassID = 12345
INT isOverButton = 0

OPENWINDOW win,0,0,640,480,@MINBOX|@MAXBOX|@SIZE,0,"PNG as button",&win_handler
SETWINDOWCOLOR win,RGB(200,255,200)

hCake=Load_PNG_FromFile(GETSTARTPATH+"birthday cake normal.png",140,140,0xFFC8FFC8)
hCakeOver=Load_PNG_FromFile(GETSTARTPATH+"birthday cake over.png",140,140,0xFFC8FFC8)
hCakePressed=Load_PNG_FromFile(GETSTARTPATH+"birthday cake pressed.png",140,140,0xFFC8FFC8)
SHOWIMAGE win,hCake,@IMGBITMAP,270,50,140,140

' method #2
CONST BUTTON_1 = 1
CONTROL win,@SYSBUTTON,"",265,201,140,140,0x50000000,BUTTON_1
ButtonSetImages(win, BUTTON_1)
SetWindowSubclass(GetDlgItem(win.hwnd, BUTTON_1), &subclassButtonProc, subclassID, 0)

WAITUNTIL ISWINDOWCLOSED(win)
DELETEIMAGE hCake,@IMGBITMAP
DELETEIMAGE hCakeOver,@IMGBITMAP
DELETEIMAGE hCakePressed,@IMGBITMAP
RemoveWindowSubclass(GetDlgItem(win.hwnd, BUTTON_1), &subclassButtonProc, subclassID)
END

SUB win_handler(),INT
SELECT @MESSAGE
CASE WM_SETCURSOR
isOverButton = 0
RedrawWindow(GetDlgItem(win.hwnd, BUTTON_1), NULL, NULL, RDW_INVALIDATE)
CASE @IDCREATE
CENTERWINDOW win
CASE @IDCLOSEWINDOW
CLOSEWINDOW win
CASE @IDCONTROL
SELECT @CONTROLID
CASE BUTTON_1
IF @NOTIFYCODE = 0
SETCAPTION(win,"You have just clicked the button")
ENDIF
ENDSELECT
ENDSELECT
RETURN 0
ENDSUB

SUB subclassButtonProc(hWnd:INT,uMsg:INT,wParam:INT,lParam:INT,uIdSubclass:UINT_PTR,dwRefData:DWORD_PTR),INT
SELECT uMsg
CASE WM_SETCURSOR
isOverButton = 1
RedrawWindow(hWnd, NULL, NULL, RDW_INVALIDATE)
WINDOW w
w.hwnd = hWnd
SETCURSOR(w, @CSCUSTOM, LoadCursor(NULL, IDC_HAND))
RETURN TRUE
CASE WM_DRAWITEM
IF *<DRAWITEMSTRUCT>lParam.itemState & ODS_DEFAULT THEN
DrawButtonImage(*<DRAWITEMSTRUCT>lParam.hDC, hCake, 0, 0, *<DRAWITEMSTRUCT>lParam.rcItem.right, *<DRAWITEMSTRUCT>lParam.rcItem.bottom)
ELSEIF *<DRAWITEMSTRUCT>lParam.itemState & ODS_SELECTED THEN
DrawButtonImage(*<DRAWITEMSTRUCT>lParam.hDC, hCakePressed, 0, 0, *<DRAWITEMSTRUCT>lParam.rcItem.right, *<DRAWITEMSTRUCT>lParam.rcItem.bottom)
ELSEIF isOverButton THEN
DrawButtonImage(*<DRAWITEMSTRUCT>lParam.hDC, hCakeOver, 0, 0, *<DRAWITEMSTRUCT>lParam.rcItem.right, *<DRAWITEMSTRUCT>lParam.rcItem.bottom)
ELSE
DrawButtonImage(*<DRAWITEMSTRUCT>lParam.hDC, hCake, 0, 0, *<DRAWITEMSTRUCT>lParam.rcItem.right, *<DRAWITEMSTRUCT>lParam.rcItem.bottom)
ENDIF
ENDSELECT

RETURN DefSubclassProc(hWnd, uMsg, wParam, lParam)
ENDSUB

SUB ButtonSetImages(WINDOW w, INT id)
INT handle = GetDlgItem(w.hwnd, id)
LONG style = GetWindowLong(handle,GWL_STYLE)
style = WS_CHILD | WS_VISIBLE | BS_OWNERDRAW
SetWindowLong(handle,GWL_STYLE,style)
_SendMessage(handle, WM_CHANGEUISTATE, MAKELONG(UIS_SET, UISF_HIDEFOCUS), 0)
ENDSUB

SUB DrawButtonImage(INT hDC, UINT hbit, INT x, INT y, INT width, INT height) ' alpha: 1 to 255
HDC hdcMem = CreateCompatibleDC(hDC)
    HBITMAP hbmOld = SelectObject(hdcMem, hbit)
BITMAP bm
GetObject(hbit, LEN(bm), &bm)
    StretchBlt(hDC, x, y, width, height, hdcMem, 0, 0, bm.bmWidth, bm.bmHeight, SRCCOPY)
    SelectObject(hdcMem, hbmOld)
    DeleteDC(hdcMem)
ENDSUB


Title: Re: DLL creation
Post by: Brian on September 21, 2018, 11:57:40 AM
And another one bites the dust! How did you do the "glow" effect? I used paint.net, which had the birthday cake as a shape, and then I added the colours, and saved it as a PNG. Very useful program, and there are a lot of plugins for it

Brian
Title: Re: DLL creation
Post by: fasecero on September 21, 2018, 02:00:05 PM
I have a very old copy of photoshop (I think it's a version more than 10 years old that was given for free for a limited time by adobe). I also have an old embarcadero that was given for free also, but I never liked delphi so I never touched it. I have heard good things from Paint.net, also for GIMP as alternatives. I did a kick search and found this short video to make a glow in paint.net, maybe it can help, my advice watch the video without sound, the music is terrible: https://www.youtube.com/watch?v=BR6BxROSPDg
Title: Re: DLL creation
Post by: Brian on September 22, 2018, 02:48:36 AM
Fasecero,

I remember the Photoshop giveaways. In the UK, you could buy a magazine with a CD inside, containing the full version (7, I think), no limitations, private or business use. Got to be over 10 years ago, now. Although it wasn't easy to update them later

My money-conscious MD asked me to take some money out of the petty cash, and go and buy 10 copies. I think we cleaned the town centre shops out!

I tried GIMP a few years ago, and found it very slow. I have the latest version now, and it has speeded up tremendously, although I don't think it is very friendly. Maybe I don't use it enough to give it a fair comment

Brian
Title: Re: DLL creation
Post by: fasecero on September 22, 2018, 10:43:37 AM
There was a magazine with a CD-ROM many years ago â€" so long ago I don't remember the name, it was cheap and was filled with lots of shareware and freeware programs. At that time I still didn't have internet at home so I spent hours installing apps. In there I found out by pure chance a program called darkbasic that was able to make games, so I began to program. I was very bad at making games lol so I start to ask myself how traditional programs were made. Can't remember what I wrote in google but Creative Basic was the first result I got. MSDN guided me to study some c, but I only feel comfortable writting code with our tools in here :) Anyway sorry to get off track. I'm now interested in making a button with a glow effect, I mean with a transition or animation, no idea what it takes so we'll see
Title: Re: DLL creation
Post by: Brian on September 22, 2018, 11:22:23 AM
There are some plugins for paint.net which includes a button maker - it's pretty good and easy. So easy, even I made one!

https://forums.getpaint.net/topic/7186-madjik-all-plugins-last-updated-2018-04-07/

Brian
Title: Re: DLL creation
Post by: h3kt0r on October 11, 2018, 03:12:24 PM
Thanx for sharing this !

Found this site some time ago :

(http://cd.textfiles.com/images/cddrive.jpg) (http://cd.textfiles.com/)

Quote
Who knew that the companies looking for a quick buck through the late 1980's and early 1990's with "Shovelware" CDs would become the unwitting archivists of the BBS age? No one did, but here we are, looking back, muttering thanks to these souless con artists as we plunder the very data they themselves took from a time now past.

if you're looking for some oldies sharewares then, this is the place to go...