CnC_Red_Alert/WINVQ/VPLAY32/PLYVQA32.CPP

1020 lines
24 KiB
C++
Raw Normal View History

/*
** Command & Conquer Red Alert(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/****************************************************************************
*
* C O N F I D E N T I A L -- W E S T W O O D S T U D I O S
*
*----------------------------------------------------------------------------
*
* PROJECT
* VQA stand-alone player. (32Bit protected mode)
*
* FILE
* Plyvqa32.c
*
* DESCRIPTION
* This program is a stand-alone VQA Player, as well as an example of how
* to incorporate VQAPlay into a program.
*
* PROGRAMMER
* Denzil E. Long, Jr.
*
* DATE
* July 7, 1995
*
*----------------------------------------------------------------------------
*
* FUNCTIONS
* main - Standard 'C' entry point.
* Usage - Display usage information.
* Options - Parse user options.
* Find_File_Name - Find a filename on the command line.
* Print_Play_Stats - Print player statistics.
* Check_Key - Check keyboard for keypress.
* Get_Key - Get a key from the keyboard buffer.
* HardErr_Handler - Hardware error handle.
*
****************************************************************************/
#define CAPTIONS 0
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <conio.h>
#include <sys\timeb.h>
#include <mem.h>
#include <io.h>
#include <ctype.h>
#include <process.h>
#include <dos.h>
#include <vqa32\vqaplay.h>
#include <vqa32\vqafile.h>
#include <vqm32\all.h>
#if(CAPTIONS)
#include <vqm32\font.h>
#endif
/*---------------------------------------------------------------------------
* PRIVATE DECLARATIONS
*-------------------------------------------------------------------------*/
long OutputStats = 0;
long OutputTime = 0;
long LastKey;
/* Embedded version string */
char VerTag[] = {"$VER$VQAPlay 2.6 (07/07/95) 32Bit"};
char ReqTag[] = {"$REQ$VQA32 Version 2.37 & VQM32 Version 2.11 or better."};
/* Prototypes */
void main(long argc, char **argv);
static void Usage(long showall);
static void Options(long argc, char *argv[], VQAConfig *config);
static char *Find_File_Name(long argc, char *argv[], char *desired_ext);
static char *GetFilePart(char *path);
void Print_Play_Stats(VQAConfig *config, VQAStatistics *stats);
long VQCallback(unsigned char *screen, long framenum);
#ifdef __cplusplus
extern "C" {
#endif
int __cdecl Check_Key(void);
int __cdecl Get_Key(void);
#ifdef __cplusplus
}
#endif
#ifndef __WATCOMC__
#if(0)
int HardErr_Handler(int errval, int ax, int bp, int si);
#endif
#else
int __far HardErr_Handler(unsigned deverror, unsigned errcode,
unsigned __far *devhdr);
#endif
/****************************************************************************
*
* NAME
* main - Standard 'C' entry point.
*
* SYNOPSIS
* main(ArgC, ArgV)
*
* void main(short, char *[]);
*
* FUNCTION
* Initial 'C' user-routine called by startup code.
*
* INPUTS
* ArgC - Argument count, number of arguments passed in.
* ArgV - Argument array, pointers to arguments.
*
* RESULT
* NONE
*
****************************************************************************/
void main(long argc, char **argv)
{
VQAConfig myconfig;
VQAStatistics stats;
VQAHandle *vqa;
char *name;
long i;
/* Parse command-line */
if (argc < 2) {
Usage(1);
exit(0);
}
/* Get filename */
name = Find_File_Name(argc, argv, ".VQA");
if (name == NULL) {
Usage(1);
exit(0);
}
/*-------------------------------------------------------------------------
* INITIALIZE PLAYBACK CONFIGURATION
*-----------------------------------------------------------------------*/
VQA_DefaultConfig(&myconfig);
myconfig.HMIBufSize = 2048*2;
#if(CAPTIONS)
myconfig.CapFont = (char *)Load_Font("caption.fnt");
myconfig.OptionFlags |= VQAOPTF_CAPTIONS;
Set_Font(myconfig.CapFont);
SetDAC(251,255,255,255); // WHITE
SetDAC(252,255,000,000); // RED
SetDAC(253,000,255,000); // GREEN
SetDAC(254,0,0,0);
SetDAC(255,255,000,255); // CYCLE
#endif
/* Configure player with command-line */
Options(argc, argv, &myconfig);
/*-------------------------------------------------------------------------
* INSTALL THE CUSTOM CRITICAL ERROR HANDLER
*-----------------------------------------------------------------------*/
#ifndef __WATCOMC__
#if(0)
harderr(HardErr_Handler);
#endif
#else
_harderr(HardErr_Handler);
#endif
/*-------------------------------------------------------------------------
* SET THE VIDEO MODE AND VBI POLARITY BIT
*-----------------------------------------------------------------------*/
SetVideoMode(myconfig.Vmode);
myconfig.VBIBit = GetVBIBit();
/*-------------------------------------------------------------------------
* PLAY THE MOVIE
*-----------------------------------------------------------------------*/
/* Allocate a VQA handle. */
if ((vqa = VQA_Alloc()) != NULL) {
/* Initialize the handle as a standard DOS handle. */
VQA_InitAsDOS(vqa);
/* Open the movie for playback. */
if (VQA_Open(vqa, name, &myconfig) == 0) {
#if(1)
VQA_Reset(vqa);
VQA_SetStop(vqa, 100);
VQA_SeekFrame(vqa, 50, 0);
for (i = 0; i < 3; i++) {
/* Actually set the movie off. */
VQA_Play(vqa, VQAMODE_RUN);
VQA_Reset(vqa);
VQA_SeekFrame(vqa, 50, 0);
}
#else
{
long done = 0;
long mode = VQAMODE_WALK;
long key;
do {
switch (VQA_Play(vqa, mode)) {
case VQAERR_NONE:
case VQAERR_PAUSED:
case VQAERR_NOT_TIME:
case VQAERR_SLEEPING:
key = Check_Key();
if (key != 0) {
switch (key) {
case ' ':
mode = VQAMODE_PAUSE;
break;
case 0x1B:
done = 1;
break;
default:
mode = VQAMODE_WALK;
break;
}
}
break;
default:
done = 1;
break;
}
} while (done == 0);
}
#endif
/* Retrieve playback statistics (FPS, time, etc...) */
VQA_GetStats(vqa, &stats);
/* Close the movie. */
VQA_Close(vqa);
}
/* Free the VQA handle. */
VQA_Free(vqa);
}
/*-------------------------------------------------------------------------
* RESTORE DISPLAY TO TEXT AND PRINT STATISTICS
*-----------------------------------------------------------------------*/
SetVideoMode(TEXT);
#if(CAPTIONS)
if (myconfig.CapFont != NULL) free(myconfig.CapFont);
#endif
Usage(0);
/* Print play statistics */
if (OutputStats) {
printf("Movie Name: %s\n",strupr(name));
Print_Play_Stats(&myconfig, &stats);
}
exit(0);
}
/****************************************************************************
*
* NAME
* Usage - Display usage information.
*
* SYNOPSIS
* Usage()
*
* void Usage(void);
*
* FUNCTION
*
* INPUTS
* NONE
*
* RESULT
* NONE
*
****************************************************************************/
static void Usage(long showall)
{
printf("\n%s Copyright (c) 1995 Westwood Studios.\n", &VerTag[5]);
printf("Playback library: %s\n", VQA_IDString());
puts("Written by Denzil E. Long, Jr.\n");
if (showall) {
puts("Usage: VPLAY <filename> [options]");
puts(" -z: Single-step");
puts(" -d: No drawing");
puts(" -fn: Draw at 'n' frames per second");
puts(" -ln: Load at 'n' frames per second");
puts(" -b: Use buffered video");
puts(" -o: Output play statistics");
puts(" -p: Enable slow palette setting");
puts(" -w: Enable woofer drawing.");
#if(VQAMONO_ON)
puts(" -m: Enable mono screen output");
#endif
#if(VQAAUDIO_ON)
puts(" -a: Audio playback rate");
puts(" -ac: Compatibility mode (Force SoundBlaster).");
puts(" -alt: Play alternate audio track.");
puts(" -s: Disable sound");
#if(VQAVOC_ON)
puts(" -cname: Name of VOC file to play instead of interleaved audio");
#endif
#endif
puts(" -t_: Timer method:");
puts(" a = Audio DMA position");
puts(" i = Interrupt");
puts(" d = DOS");
puts(" -v_: Video mode:");
#if(VQAMCGA_ON)
puts(" m = MCGA");
#endif
#if(VQAXMODE_ON)
puts(" w = XMODE 320x200");
puts(" x = XMODE 320x240");
puts(" y = XMODE 320x200, VRAM mode");
puts(" z = XMODE 320x240, VRAM mode");
#endif
#if(VQAVESA_ON)
puts(" u = VESA 320x200");
puts(" v = VESA 640x480 in a window (buffered only)");
puts(" s = VESA 640x480 scaled to 640x400 (buffered only)");
#endif
}
}
/****************************************************************************
*
* NAME
* Options - Parse user options.
*
* SYNOPSIS
* Options(ArgC, ArgV, Anim)
*
* void Options(long, char *[], VQAnim *);
*
* FUNCTION
*
* INPUTS
* ArgC - Argument count, same as passed to main().
* ArgV - Argument array, same as passed to main().
* Anim - Pointer to VQAnim structure.
*
* RESULT
* NONE
*
****************************************************************************/
static void Options(long argc, char *argv[], VQAConfig *config)
{
long l;
long i;
/* Scan arguments for any options ( / or - followed by a letter) */
for (l = 0; l < argc; l++) {
if (argv[l][0] == '/' || argv[l][0] == '-') {
/* Convert the argument to uppercase. */
for (i = 1; argv[l][i] != 0; i++) {
argv[l][i] = toupper(argv[l][i]);
}
switch (argv[l][1]) {
/* Set Audio Playback rate */
#if(VQAAUDIO_ON)
case 'A':
if (isdigit(argv[l][2])) {
config->AudioRate = atoi(&argv[l][2]);
} else {
if (argv[l][2] == 'C') {
config->DigiCard = 0xE000;
} else {
if (strcmpi(&argv[l][2], "LT") == 0) {
config->OptionFlags |= VQAOPTF_ALTAUDIO;
}
}
}
break;
#endif
/* Turn off UnVQ to screen */
case 'B':
config->DrawFlags |= VQACFGF_BUFFER;
break;
/* VOC File Name */
#if(VQAAUDIO_ON && VQAVOC_ON)
case 'C':
config->VocFile = (argv[l] + 2);
break;
#endif
/* Turn off vertical blank wait */
case 'D':
config->DrawFlags |= VQACFGF_NODRAW;
break;
/* Set Drawer's frame rate */
case 'F':
config->DrawRate = atoi(&argv[l][2]);
break;
/* Set Loader's frame rate */
case 'L':
config->FrameRate = atoi(&argv[l][2]);
break;
/* Mono mode */
#if(VQAMONO_ON)
case 'M':
config->OptionFlags |= VQAOPTF_MONO;
break;
#endif
/* Output statistics */
case 'O':
OutputStats = 1;
break;
/* Slow palette mode */
case 'P':
config->OptionFlags |= VQAOPTF_SLOWPAL;
break;
/* Run with no sound */
#if(VQAAUDIO_ON)
case 'S':
config->OptionFlags &= (~VQAOPTF_AUDIO);
break;
#endif
case 'T':
switch (argv[l][2]) {
case 'A':
config->TimerMethod = VQA_TMETHOD_AUDIO;
break;
case 'I':
config->TimerMethod = VQA_TMETHOD_INT;
break;
case 'D':
config->TimerMethod = VQA_TMETHOD_DOS;
break;
default:
break;
}
break;
/* Set video mode */
case 'V':
if (argv[l][2] == 'M') {
config->Vmode = MCGA;
}
#if(VQAXMODE_ON)
else if (argv[l][2] == 'W') {
config->Vmode = XMODE_320X200;
}
else if (argv[l][2] == 'X') {
config->Vmode = XMODE_320X240;
}
else if (argv[l][2] == 'Y') {
config->Vmode = XMODE_320X200;
config->DrawFlags |= VQACFGF_VRAMCB;
}
else if (argv[l][2] == 'Z') {
config->Vmode = XMODE_320X240;
config->DrawFlags |= VQACFGF_VRAMCB;
}
#endif
#if(VQAVESA_ON)
else if (argv[l][2] == 'V') {
config->Vmode = VESA_640X480_256;
config->DrawFlags |= VQACFGF_BUFFER;
}
else if (argv[l][2] == 'S') {
config->Vmode = VESA_640X480_256;
config->DrawFlags |= (VQACFGF_BUFFER|VQACFGF_SCALEX2);
}
else if (argv[l][2] == 'U') {
config->Vmode = VESA_320X200_32K_1;
}
#endif
else {
printf("Unsupported video mode flag: %c\n", argv[l][2]);
exit(0);
}
break;
#if(VQAWOOFER_ON)
case 'W':
config->DrawFlags |= VQACFGB_WOOFER;
break;
#endif
/* Single-step */
case 'Z':
config->OptionFlags |= VQAOPTF_STEP;
config->DrawFlags |= VQACFGF_NOSKIP;
break;
default:
break;
}
}
}
return;
}
/****************************************************************************
*
* NAME
* Find_File_Name - Find a filename on the command line.
*
* SYNOPSIS
* Name = Fine_File_Name(ArgC, ArgV, Ext, Anim)
*
* char * Fine_File_Name(short, char *[], char *, VQAnim);
*
* FUNCTION
* Finds a file name on the command line, excluding anything with "/" or
* "-" on it. The given extension is added to the name if there is no
* extension in the filename.
*
* INPUTS
* ArgC - Argument count same as in main().
* ArgV - Argument array same as in main().
* Ext - Pointer to filename extension.
* Anim - Pointer to VQAnim structure.
*
* RESULT
* Name = Pointer to filename.
*
****************************************************************************/
static char *Find_File_Name(long argc, char *argv[], char *desired_ext)
{
long opt = 1;
static char drive[_MAX_DRIVE] = {0};
static char dir[_MAX_DIR] = {0};
static char fname[_MAX_FNAME] = {0};
static char ext[_MAX_EXT] = {0};
static char pathname[_MAX_PATH] = {0};
/* Search for a non '-' option */
while ((argv[opt][0] == '/') || (argv[opt][0] == '-')) {
opt++;
}
if (argc == opt) {
return (NULL);
}
/* Split the filename into its components */
_splitpath(argv[opt],drive,dir,fname,ext);
if (strlen(ext) == 0) {
strcpy(ext,desired_ext);
}
/* Rebuild the complete filename */
_makepath(pathname, drive, dir, fname, ext);
return (pathname);
}
/****************************************************************************
*
* NAME
* Print_Play_Stats - Print player statistics.
*
* SYNOPSIS
* Print_Play_Stats(Anim)
*
* void Print_Play_Stats(VQAnim);
*
* FUNCTION
*
* INPUTS
* Anim - Pointer to VQAnim structure.
*
* RESULT
* NONE
*
****************************************************************************/
void Print_Play_Stats(VQAConfig *config, VQAStatistics *stats)
{
long tim1,tim2;
long fps1,fps2;
/* Video mode name */
printf("Video Mode: ");
if (config->Vmode == MCGA) {
if (config->DrawFlags & VQACFGF_BUFFER) {
puts("MCGA Buffered");
} else {
puts("MCGA");
}
}
#if(VQAXMODE_ON)
else if (config->Vmode == XMODE_320X200) {
if (config->DrawFlags & VQACFGF_BUFFER) {
puts("XMODE 320x200 Buffered");
} else {
if (config->DrawFlags & VQACFGF_VRAMCB) {
puts("XMODE 320x200 VRAM");
} else {
puts("XMODE 320x200");
}
}
}
else if (config->Vmode == XMODE_320X240) {
if (config->DrawFlags & VQACFGF_BUFFER) {
puts("XMODE 320x240 Buffered");
} else {
if (config->DrawFlags & VQACFGF_VRAMCB) {
puts("XMODE 320x240 VRAM");
} else {
puts("XMODE 320x240");
}
}
}
#endif
#if(VQAVESA_ON)
else if (config->Vmode == VESA_640X480_256) {
if (config->DrawFlags & VQACFGF_SCALEX2) {
puts("VESA 640x480 Scaled");
} else {
puts("VESA 640x480 Windowed");
}
} else if (config->Vmode==VESA_320X200_32K_1) {
if (config->DrawFlags & VQACFGF_BUFFER) {
puts("VESA 320x200 Buffered");
} else {
puts("VESA 320x200");
}
}
#endif
else {
puts("UNKNOWN");
}
printf("%lu bytes used.\n", stats->MemUsed);
tim1 = ((stats->EndTime - stats->StartTime) / VQA_TIMETICKS);
tim2 = (((stats->EndTime - stats->StartTime) * 10) / VQA_TIMETICKS);
printf("Elapsed time %d.%d seconds.\n", tim1, tim2 - (tim1 * 10));
printf("%u frames loaded.\n", stats->FramesLoaded);
printf("%u frames drawn.\n", stats->FramesDrawn);
printf("%u frames skipped.\n", stats->FramesSkipped);
/* Frame rates */
fps1 = ((stats->FramesLoaded * VQA_TIMETICKS)
/ (stats->EndTime - stats->StartTime));
fps2 = (stats->FramesLoaded * VQA_TIMETICKS * 10)
/ (stats->EndTime - stats->StartTime);
printf("Load rate: %d.%d FPS\n", fps1, fps2 - (fps1 * 10));
fps1 = (stats->FramesDrawn * VQA_TIMETICKS)
/ (stats->EndTime -stats->StartTime);
fps2 = (stats->FramesDrawn * VQA_TIMETICKS * 10)
/ (stats->EndTime - stats->StartTime);
printf("Draw rate: %d.%d FPS\n", fps1, fps2 - (fps1 * 10));
/* Audio */
printf("Audio samples played: %lu\n", stats->SamplesPlayed);
}
/****************************************************************************
*
* NAME
* Check_Key - Check keyboard for keypress.
*
* SYNOPSIS
* Key = Check_Key()
*
* short Check_Key(void);
*
* FUNCTION
*
* INPUTS
* NONE
*
* RESULT
* Key - Value of key pressed.
*
****************************************************************************/
int Check_Key(void)
{
if (kbhit()) {
LastKey = getch();
return (LastKey);
} else {
return (0);
}
}
/****************************************************************************
*
* NAME
* Get_Key - Get a key from the keyboard buffer.
*
* SYNOPSIS
* Key = Get_Key()
*
* short Get_Key(void);
*
* FUNCTION
*
* INPUTS
* NONE
*
* RESULT
* Key - Value of key.
*
****************************************************************************/
int Get_Key(void)
{
return (LastKey);
}
#ifndef __WATCOMC__
#if(0)
/****************************************************************************
*
* NAME
* HardErr_Handler - Hardware error handle.
*
* SYNOPSIS
* HardErr_Handler(Error, AX, BP, SI)
*
* int HardErr_Handler(int, int, int, int);
*
* FUNCTION
* DOS calls 1 through 0xc are OK; any other will corrupt DOS.
* Important safety tip: The arguments for this function are NOT the
* same as those for the Microsoft '_harderr()' function.
*
* INPUTS
* Error - Error type value. Low order byte can be:
*
* 0 = Attempt to write to write-protected disk.
* 1 = Unknown unit.
* 2 = Drive not ready.
* 3 = Unknown command.
* 4 = CRC error.
* 5 = Bad drive-request structure length.
* 6 = Seek error.
* 7 = Unknown media type.
* 8 = Sector not found.
* 9 = Printer out of paper.
* 10 = Write fault.
* 11 = Read fault.
* 12 = General failure.
*
* AX - Will be non-negative if this is a disk error, negative
* otherwise. low-order byte of ax gives failing drive number
* high bits:
*
* 15 0 = disk error
* 14 not used
* 13 0 = "Ignore" not allowed
* 12 0 = "Retry" not allowed
* 11 0 = "Fail" not allowed (fail is same as abort)
* 10,9:
* 00 DOS
* 01 File allocation table
* 10 Directory
* 11 Data area
* 8 0 = Read error, 1 = Write error
*
* BP,SI - Not used
*
* RESULT
* NONE
*
****************************************************************************/
int HardErr_Handler(int errval, int ax, int bp, int si)
{
/* Suppress compiler warnings */
errval = errval;
bp = bp;
si = si;
/* If AX < 0, this was not a disk error, so return the ABORT code */
if (ax < 0) {
hardretn(_HARDERR_ABORT);
}
/* Otherwise, if this is a drive-not-ready error, retry 5 times */
if (kbhit() != 0) {
if (ax & (1 << 13)) {
hardresume(_HARDERR_IGNORE);
} else if (ax & (1 << 11)) {
hardresume(_HARDERR_FAIL);
} else {
hardresume(_HARDERR_RETRY);
}
} else {
if (ax & (1 << 12)) {
hardresume(_HARDERR_RETRY);
} else {
hardresume(_HARDERR_IGNORE);
}
}
return (0);
}
#endif
#else
/****************************************************************************
*
* NAME
* HardErr_Handler - Critical error handler for INT 0x24.
*
* SYNOPSIS
* Action = HardErr_Handler(DeviceError, ErrorCode, DeviceHeader)
*
* int HardErr_Handler(unsigned, unsigned, unsigned __far *);
*
* FUNCTION
*
* INPUTS
* DeviceError - Device error description.
*
* bit 15 0 indicates disk error.
* bit 14 not used
* bit 13 0 indicates "Ignore" response not allowed.
* bit 12 0 indicates "Retry" response not allowed.
* bit 11 0 indicates "Fail" response not allowed.
* bit 9,10 location of error.
*
* Value Meaning
*
* 00 MS-DOS
* 01 File Allocation Table (FAT)
* 10 Directory
* 11 Data area
*
* bit 8 0 indicates read error,1 indicates write error
*
* The low-order byte indicates the drive where the error
* occurred; (0 = drive A, 1 = drive B, etc.).
*
* ErrorCode - Type of error.
*
* The low-order byte can be one of the following values:
*
* 0x00 Attempt to write to a write-protected disk.
* 0x01 Unknown unit.
* 0x02 Drive not ready.
* 0x03 Unknown command.
* 0x04 CRC error in data.
* 0x05 Bad drive-request structure length.
* 0x06 Seek error.
* 0x07 Unknown media type.
* 0x08 Sector not found.
* 0x09 Printer out of paper.
* 0x0A Write fault.
* 0x0B Read fault.
* 0x0C General fault.
*
* DeviceHeader - Pointer to a device header control-block that contains
* information about the device on which the error
* occurred.
*
* RESULT
* Action - Indication of what action to take using one of the following
* values:
*
* Value Meaning
*
* _HARDERR_IGNORE Ignore the error.
* _HARDERR_RETRY Retry the operation.
* _HARDERR_ABORT Abort the program issuing INT 0x23
* _HARDERR_FAIL Fail the system call that is in progress
* (DOS 3.0 or higher)
*
****************************************************************************/
int __far HardErr_Handler(unsigned deverror, unsigned errcode,
unsigned __far *devhdr)
{
/* Prevent compiler warnings. */
errcode = errcode;
devhdr = devhdr;
/* If this is not a disk error, then return the ABORT code. */
if (deverror & (1 << 15)) {
return (_HARDERR_ABORT);
}
/* If this is a drive-not-ready error then continue to retry. */
if (kbhit() != 0) {
if (deverror & (1 << 13)) {
_hardresume(_HARDERR_IGNORE);
}
else if (deverror & (1 << 11)) {
_hardresume(_HARDERR_FAIL);
}
else {
_hardresume(_HARDERR_RETRY);
}
} else {
if (deverror & (1 << 12)) {
_hardresume(_HARDERR_RETRY);
} else {
_hardresume(_HARDERR_IGNORE);
}
}
return (0);
}
#endif
/****************************************************************************
*
* NAME
* GetFilePart - Get the filename part of path/filename.
*
* SYNOPSIS
* Filename = GetFilePart(Path)
*
* char *GetFilePart(char *);
*
* FUNCTION
*
* INPUTS
* Path - Full path to retrieve filename from.
*
* RESULT
* Filename - Pointer to filename or NULL if no filename.
*
****************************************************************************/
static char *GetFilePart(char *path)
{
char *ptr;
ptr = strrchr(path, '\\');
if (ptr == NULL) {
ptr = strrchr(path, ':');
}
if (strlen(ptr) > 1) {
ptr++;
}
return (ptr);
}