Archived

This topic is now archived and is closed to further replies.

funkman

CD checker

Recommended Posts

S1CA    1418
Here''s (roughly) what I did for the CD check in the engine for Pac-Man:Adventures in Time (and a few other PC titles which used the same engine):


1. Put a fairly unique file in the root of your CD/DVD (say KEWLGAME.DAT), with some unique contents (a GUID for example).

2. Use GetLogicalDrives() or GetLogicalDriveStrings
() with GetDriveType() to determine which drives in the system are CD/DVD drives (remember there may be more than one, and the user may have a multichanger). Do this every time your app starts - users do change drive letters around.

3. Before doing any CD access call SetErrorMode() to prevent blue screens and error boxes when the system can''t read from a CD/DVD that''s still mounting.

4. If your software has been run previously, set up a registry key with the "last known" drive location of the game CD. ALWAYS check that location first - if you don''t, people with multichangers will hate you if the game CD is in the last of their mounted drives.

5. For each CD/DVD drive (starting at the one in the registry - see above), try to open your file, then try to read from your file and finally compare the contents of the file with your "unique" contents from step 1.

6. IMO it''s better to use the Win32 API calls directly here rather than fopen()/iostream stuff because you get much more control and much more informative error codes back.

7. Don''t rely on AutoRun or WM_DEVICECHANGE notifications - many users disable them. Sometimes properly (so WM_DEVICECHANGE still works and AutoRun doesn''t), often incorrectly (so neither work).


--
Simon O''Connor
3D Game Programmer &
Microsoft DirectX MVP

Share this post


Link to post
Share on other sites
funkman    122
I''m struggling a bit with my cd-checker. Could you possibly give me a short sample code.

Cheers

P.S. oh yes sorry for crossposting, I was a bit desperate

Share this post


Link to post
Share on other sites
Guest Anonymous Poster   
Guest Anonymous Poster
Try this, I wrote it for a PC game back in ''99 so it might not be everything you want (although it performs most of Simon''s checklist items):


int CDInDrive(char *signature, char *dest)
{
BYTE buffer[260], *p;

// Get a selection of drive strings

ZeroMemory(buffer, 260);
GetLogicalDriveStrings(260, buffer);

for (p = buffer; *p || *(p + 1); )
{
BYTE path[260];
FILE *fp;

// Construct a path to the CD Information File

wsprintf(path, "%s%s", p, "CDINFO.FIL");

// Validate that it is a CD drive (stops the A: thrashing)

if (GetDriveType(p) == DRIVE_CDROM)
{
// Attempt to open it

if ((fp = fopen(path, "rb")) != NULL)
{
BYTE instore[48];
int s;

// Can the title be read?

if (fread(instore, 1, 19, fp) == 19)
{
instore[19] = 0;

// Is it the correct title?

if (!strcmp(instore, "CD Information File"))
{
// Seek past the CR/LF

fseek(fp, 2, SEEK_CUR);
s = strlen(signature);

// Can the signature be read?

if (fread(instore, 1, s, fp) == 31)
{
instore[s] = 0;

// Is it the correct signature?

if (!strcmp(instore, signature))
{
// Copy the drive string if it''s desired

if (dest != NULL) strcpy(dest, p);

// CD is in a drive!

return (1);
}
}
}
}

fclose(fp);
}
}

// Move onto the next drive string

while (*p++) { }
}

return (0);
}



It managed to get through Infogrames test department fine (which is amazingly thourough) although some of points Simon brought up would be worth extending to

Share this post


Link to post
Share on other sites
funkman    122
Managed to figure out myself but thanks a million anyway.
I should be hanging around more oten in here, might make my life a lot easier.

One more question. I wonder if anyone had any experience using NDL middleware NetImmerse (or Gamebryo as it is called now).

Again appreciate your help.

Cheers

Share this post


Link to post
Share on other sites
funkman    122
Sorry, another CD checker related question.
How do I you control the message box that appears when the CD is not in the drive. Currently if I press "Continue" it will launch the game anyway?

Share this post


Link to post
Share on other sites
You can check the return value of the message box, however in most instances you really only need 2 buttons: one for try again, and one for exit. If you put up a message box, check its return value for the exit code. If exit was pressed, tell the program to quit. If try again was pressed, run the cd check again.

Share this post


Link to post
Share on other sites
funkman    122
The thing is its a standard Windows message box, I''m just checking if the CD is in and if its not Windows does the rest. However for some bizzare reason if i press Continue or even Cancel it still starts the game

Share this post


Link to post
Share on other sites
funkman    122
Just to explain a bit more. Lets take as an example code posted above by Anonymous. Mine is very similar in structure and if you implement and run it, when CD is not in the CD-ROM drive it will display a standard windows message box "No CD" and no matter what you press it will still launch the game.

Share this post


Link to post
Share on other sites
S1CA    1418
So the "No CD" is a message box YOU ask Windows to display...
...i.e. by calling MessageBox() or similar ?

If so, it''s very likely there''s something wrong with how you handle the return code from the MessageBox() function. Post the relevent code (i.e. the bit that calls MessageBox() and decides what to do based on its return code) and it should be pretty easy to determine where you''re going wrong.


If your code isn''t asking Windows to display a message box, and it''s something Windows has produced, then see point 3 of my first reply.

--
Simon O''Connor
3D Game Programmer &
Microsoft DirectX MVP

Share this post


Link to post
Share on other sites
funkman    122
More problems
Seems like I''m missing something realy stupid and going around in a continious circle. I thought that the code posted earlier might be better then mine and solve problems so I modified it

int CDInDrive(char *signature, char *dest)
{
char buffer[260], *p;

//lets get a selection of drive strings

ZeroMemory(buffer, 260);
GetLogicalDriveStrings(260, buffer);
SetErrorMode (SEM_NOOPENFILEERRORBOX);

for (p=buffer; *p||*(p+1);)
{
char path[260];
FILE *fp;

//construct a path to the CD Information File

wsprintf(path, "%s%s", p, "aa.dat");

//validate that it is a CD drive (stops the A: thrashing)

if (GetDriveType(p) == DRIVE_CDROM)
{
//Attemp to open it

if ((fp=fopen(path, "rb")) != NULL)
{
char instore[48];
int s;

//Can the title be read?

if (fread(instore, 1, 19, fp)==19)
{
instore[19]=0;

//is it the correct title?

if(!strcmp(instore, "AAA"))
{
//seek past the CR/LF

fseek(fp, 2, SEEK_CUR);
s=strlen(signature);

//can the signature be read?

if (fread(instore, 1, s, fp) ==31)
{
instore[s]=0;

//is it the correct signature

if (!strcmp(instore, signature))
{
//copy the drive string if its desired

if (dest !=NULL) strcpy(dest, p);

//CD is in a drive!

return (1);
}
}
}

}

fclose(fp);
}
}
//Move onto the next drive string

while (*p++){}
}
return (0);
}


Then I''m calling CDInDrive("AAA", "d:\\"); at the beggining of my app. It doesnt read the file and as long as there us any CD in the drive the game will run. If there is no CD at all it will display the message box but no matter what you press the game still runs. Could you point me my mistakes.

Thanks for your help

P.S I know you warned me about using fopen() but it should work anyway. If I''ll make this work I will proceed further

Share this post


Link to post
Share on other sites
S1CA    1418
Can you post the piece of code that actually calls CDInDrive(), in particular the piece of code which displays the message box. The actual "check" code looks like its doing its job (from a quick scan) - I think it''s what you''re doing in response to the CD not being in the drive that''s at fault.

You should have something like (pseudo-C):


blah main()
{
startUpStuff();

// loop until the CD is found OR the user chooses to cancel
while (CDinDrive(blah)==0)
{
// no CD found, throw up error for user to decide
ok_cancel = MessageBox( "No CD in the drive", blah );
if (ok_cancel==CANCEL)
{
// user clicked cancel to exit the app .
cleanUpAnythingAllocated();
// exit from main() (or WinMain etc).
return -1;
}

// if we get here, then the user clicked OK instead of cancel
// so we go round the loop and check for the CD again.
}

runTheGame();

return 0;
}



It''s also definately worth single stepping your code in the debugger to see exactly what''s happening at every stage - that''s a very important skill to get used to.

Share this post


Link to post
Share on other sites
funkman    122
Here is what I am calling

Winmain (blah blah)

CDInDrive("BBB", "d:\\");

int ok_cancel;

while (CDInDrive("BBB", "d:\\") == 0)
{
ok_cancel = MessageBox (hMain, "Please Insert CD", "blah", MB_OKCANCEL | MB_ICONEXCLAMATION);
if (ok_cancel == IDCANCEL)
{
// user clicked cancel to exit the app

DestroyWindow(hMain);
return -1;

}
}


then all the usual game stuff load up, cutscene, levels etc.
The CD doesnt even blink

Share this post


Link to post
Share on other sites
S1CA    1418
Try putting a breakpoint on the "if (ok_cancel == IDCANCEL)" line and see if it ever gets hit. If it does, step through the code to see where that "return -1" returns to.

That code will only work if it''s inside the WinMain() or main() function - i.e. so that the return -1 returns from WinMain()/main().


The CD light doesn''t need to blink, nor does the drive even need to spin to check if the CD is in - if you haven''t changed the CD, Windows keeps the contents and commonly/recently read files cached. When you insert a new CD, it checks to see if its the same as the old CD and will often still use the cached contents if it is.

If you use CreateFile() rather than fopen() you have more control over whether caching (etc) is used.

Share this post


Link to post
Share on other sites
funkman    122
Just to get it clear. What is *signature and *destination in my CDInDrive? Is signature the name of the disk or a text in a specified file? For destination I just put drive letter? Since the original code for CDInDrive is checking for a file "aa.dat"
Also I do not fully understand what is this looking for -
if(!strcmp(instore, "AAA"). When i call fopen it has "rb" parameters - thats binary read only right? Think my problem is in actual CD now

Share this post


Link to post
Share on other sites
funkman    122
Just to get it clear. What is *signature and *destination in my CDInDrive? Is signature the name of the disk or a text in a specified file? For destination I just put drive letter? Since the original code for CDInDrive is checking for a file "aa.dat"
Also I do not fully understand what is this looking for -
if(!strcmp(instore, "AAA"). When i call fopen it has "rb" parameters - thats binary read only right? Think my problem is in actual CD now

Share this post


Link to post
Share on other sites
funkman    122
Also my machine has 3 CD drives E: F: G: If I put the CD in one of those drives the next time I will launch my app it will search for a CD in that drive and wouldnt look for it in any other. Guess I''m not clearing something

Share this post


Link to post
Share on other sites
Guest Anonymous Poster   
Guest Anonymous Poster
Yikes, that code is years old - I thought I could just copy&paste and all would be happy. But it does deserve a little explanation

I stored a file on the CD called CDINFO.FIL that contained two lines (CR/LF text format):


CD Information File
Lucky Luke On The Daltons Trail


The signature is the second line to look for and compare with (so it can be reused with different games). dest is where the code should stick the name of the CD drive (looking back the parameters should be const char*, char* - passing a string literal as a second parameter isn''t a good idea). The code that called it was in a tiny executable stub that chained to the main game:


// Keep looping until the CD is inserted or cancel is selected

while (!CDInDrive("Lucky Luke On The Daltons Trail", NULL))
{
retval = MessageBox(hWnd, LanguageText[TEXT_CDINDRIVE_DIRECTX], LanguageText[TEXT_WARNING], MB_RETRYCANCEL);

if (retval == IDCANCEL)
exit(-1);
}


I''m sorry I can''t provide help beyond that, I have forgone all my PC programming sins in favour of consoles.

Share this post


Link to post
Share on other sites
funkman    122
Dont want to seem stupid, but is CR/LF (carriege return/line feed ?)format just a standard text format. Could be created in Wordpad etc?

Thanks again for help

Share this post


Link to post
Share on other sites
Guest Anonymous Poster   
Guest Anonymous Poster
yeah, notepad writes that format (as does DOS edit.com). If you''re unsure just open your text file in a hex editor (edit /78 file.txt ).

Share this post


Link to post
Share on other sites