Files
Descent3/Descent3/PilotPicsAPI.cpp
2024-05-30 03:35:17 -04:00

749 lines
21 KiB
C++

/*
* Descent 3
* Copyright (C) 2024 Parallax Software
*
* 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/>.
--- HISTORICAL COMMENTS FOLLOW ---
* $Logfile: /DescentIII/main/PilotPicsAPI.cpp $
* $Revision: 9 $
* $Date: 10/21/99 9:28p $
* $Author: Jeff $
*
* API implementation for Pilot Pictures
*
* $Log: /DescentIII/main/PilotPicsAPI.cpp $
*
* 9 10/21/99 9:28p Jeff
* B.A. Macintosh code merge
*
* 8 4/14/99 3:57a Jeff
* fixed case mismatch in #includes
*
* 7 3/30/99 4:26a Jeff
* reset variable when closing down system
*
* 6 3/29/99 7:19p Jeff
* increased buffer for importing pilot pics
*
* 5 2/05/99 7:04p Jeff
* table file parsing macros put in
*
* 4 1/21/99 11:15p Jeff
* pulled out some structs and defines from header files and moved them
* into seperate header files so that multiplayer dlls don't require major
* game headers, just those new headers. Side effect is a shorter build
* time. Also cleaned up some header file #includes that weren't needed.
* This affected polymodel.h, object.h, player.h, vecmat.h, room.h,
* manage.h and multi.h
*
* 3 11/24/98 5:16p Jeff
* fixed bugs in the api during initial testing
*
* 2 11/23/98 6:30p Jeff
* initial creation
*
* $NoKeywords: $
*/
#include <stdlib.h>
#include <stdio.h>
#include "bitmap.h"
#include "player.h"
#include "pilot.h"
#include "cfile.h"
#include "mono.h"
#include "ddio.h"
#include "manage.h"
#include "PilotPicsAPI.h"
#include "mem.h"
#include <algorithm>
#define PILOTPIC_DATABASE_HOG "PPics.Hog"
#define PILOTPIC_DATABASE_INDEX "PPics.idx"
static CFILE *PilotPic_database_index_handle = NULL;
static int PilotPic_database_hog_handle = 0;
static int PilotPic_find_offset;
static char PilotPic_find_name[PILOT_STRING_SIZE];
static bool PilotPic_init = false;
// -----------------------------------------------
// PPic_BuildDatabase
// Purpose:
// Builds up some databases for fast access
// based off the index file.
// -----------------------------------------------
static bool PPic_BuildDatabases(void);
// -----------------------------------------------
// PPic_FreeDatabase
// Purpose:
// Frees up memory associated with the databases
// -----------------------------------------------
static void PPic_FreeDatabase(void);
// -------------------------------------------------
// PPic_JumpToPilot
// Purpose:
// Returns an offset into the file to where the
// the first pilot that matches pilot_name is. -1
// is returned if no match
// --------------------------------------------------
static int PPic_JumpToPilot(char *pilot_name);
// -------------------------------------
// PPic_GetIndexFromID
// Purpose:
// Returns the file index into the Pilot_id_to_offset
// list for the given id, -1 if not found.
// -------------------------------------
static int PPic_GetIndexFromID(int id);
// -------------------------------------
// PPic_GetOffsetByID
// Purpose:
// Returns the file offset of the given pilot id. -1 if not found
// -------------------------------------
static int PPic_GetOffsetByID(uint16_t pilot_id);
//===========================================================================
// ---------------------------------------------------------
// PPic_InitDatabase
// Purpose:
// Initializes the database for the pictures. Call before
// any other Pilot Picture function
// ---------------------------------------------------------
bool PPic_InitDatabase(void) {
if (PilotPic_init) {
mprintf(0, "PPIC: InitDatabase already called\n");
return true;
}
// attempt to open the hog database
// --------------------------------
char fullpath[_MAX_PATH];
ddio_MakePath(fullpath, LocalD3Dir, PILOTPIC_DATABASE_HOG, NULL);
PilotPic_database_hog_handle = cf_OpenLibrary(fullpath);
if (PilotPic_database_hog_handle == 0) {
// there was an error opening the hog database
// -----------------------------------------
mprintf(0, "PPIC: Error opening %s database\n", PILOTPIC_DATABASE_HOG);
PilotPic_database_index_handle = NULL;
return false;
}
// attempt to open the pilotpicture database index
// -----------------------------------------------
PilotPic_database_index_handle = cfopen(PILOTPIC_DATABASE_INDEX, "rb");
if (PilotPic_database_index_handle == NULL) {
// there was an error opening the database index
// ---------------------------------------------
mprintf(0, "PPIC: Error opening database index '%s'\n", PILOTPIC_DATABASE_INDEX);
cf_CloseLibrary(PilotPic_database_hog_handle);
PilotPic_database_hog_handle = 0;
return false;
}
// when we get to this point, all of our files have been opened successfully, so to
// make searching, etc fast, we'll build some databases in memory, for quick lookup
// ---------------------------------------------------------------------------------
if (!PPic_BuildDatabases()) {
// there was an error building the databases
// -----------------------------------------
mprintf(0, "PPIC: Error building databases\n");
cfclose(PilotPic_database_index_handle);
PilotPic_database_index_handle = NULL;
cf_CloseLibrary(PilotPic_database_hog_handle);
PilotPic_database_hog_handle = 0;
return false;
}
PilotPic_init = true;
atexit(PPic_CloseDatabase);
return true;
}
// ---------------------------------------------------------
// PPic_CloseDatabase
// Purpose:
// Closes down the picture database.
// ---------------------------------------------------------
void PPic_CloseDatabase(void) {
if (!PilotPic_init)
return;
// close up the database
PPic_FreeDatabase();
// close up files
if (PilotPic_database_index_handle) {
cfclose(PilotPic_database_index_handle);
PilotPic_database_index_handle = NULL;
}
if (PilotPic_database_hog_handle) {
cf_CloseLibrary(PilotPic_database_hog_handle);
PilotPic_database_hog_handle = 0;
}
PilotPic_init = false;
}
// ---------------------------------------------------------
// PPic_QueryPilot
// Purpose:
// Given a pilot callsign it will search the database
// and return the number of pilots that match the name
// ---------------------------------------------------------
uint16_t PPic_QueryPilot(char *pilot_name) {
if (!PilotPic_init)
return 0;
int letter_offset = PPic_JumpToPilot(pilot_name);
if (letter_offset == -1)
return 0;
int count = 0;
bool done = false;
char name_buffer[PILOT_STRING_SIZE];
CFILE *file = PilotPic_database_index_handle;
// now read through the pilots until we don't get a match anymore
while (!done) {
// first read in the pilot name
uint8_t name_size;
name_size = cf_ReadByte(file);
if (cfeof(file)) {
done = true;
continue;
}
cf_ReadBytes((uint8_t *)name_buffer, name_size, file);
name_buffer[name_size] = '\0';
// next read in pilot_id
uint16_t pilot_id;
pilot_id = (uint16_t)cf_ReadShort(file);
// next read past the bitmap name
uint8_t bmp_size;
bmp_size = cf_ReadByte(file);
cfseek(file, bmp_size, SEEK_CUR);
if (stricmp(name_buffer, pilot_name)) {
// we're done
done = true;
} else
count++;
if (cfeof(file))
done = true;
}
return count;
}
// ---------------------------------------------------------
// PPic_FindFirst
// Purpose:
// Given a pilot callsign it will return the first pilot
// that matches the search. Returns true on success, false
// if it couldn't find any pilots matching. It returns
// the pilot id.
// ----------------------------------------------------------
bool PPic_FindFirst(char *pilot_name, uint16_t *pilot_id) {
if (!PilotPic_init)
return false;
*pilot_id = 65535;
int letter_offset = PPic_JumpToPilot(pilot_name);
if (letter_offset == -1)
return false;
strcpy(PilotPic_find_name, pilot_name);
char name_buffer[PILOT_STRING_SIZE];
CFILE *file = PilotPic_database_index_handle;
// first read in the pilot name
uint8_t name_size;
name_size = cf_ReadByte(file);
if (cfeof(file)) {
return false;
}
cf_ReadBytes((uint8_t *)name_buffer, name_size, file);
name_buffer[name_size] = '\0';
// next read in pilot_id
uint16_t pid;
pid = (uint16_t)cf_ReadShort(file);
// next read past the bitmap name
uint8_t bmp_size;
bmp_size = cf_ReadByte(file);
cfseek(file, bmp_size, SEEK_CUR);
if (stricmp(name_buffer, pilot_name)) {
return false; // weird
}
*pilot_id = pid;
PilotPic_find_offset = cftell(file);
return true;
}
// ----------------------------------------------------------
// PPic_FindNext
// Purpose:
// Call repeatedly until false is returned to get information
// about all the pilots that match pilot_name passed into
// an initial call to PPic_FindFirst(). It returns the
// pilot id.
// ----------------------------------------------------------
bool PPic_FindNext(uint16_t *pilot_id) {
if (!PilotPic_init)
return false;
*pilot_id = 65535;
char name_buffer[PILOT_STRING_SIZE];
CFILE *file = PilotPic_database_index_handle;
cfseek(file, PilotPic_find_offset, SEEK_SET);
if (cfeof(file)) {
return false;
}
// first read in the pilot name
uint8_t name_size;
name_size = cf_ReadByte(file);
if (cfeof(file)) {
return false;
}
cf_ReadBytes((uint8_t *)name_buffer, name_size, file);
name_buffer[name_size] = '\0';
// next read in pilot_id
uint16_t pid;
pid = (uint16_t)cf_ReadShort(file);
// next read past the bitmap name
uint8_t bmp_size;
bmp_size = cf_ReadByte(file);
cfseek(file, bmp_size, SEEK_CUR);
if (stricmp(name_buffer, PilotPic_find_name)) {
return false; // we're done
}
*pilot_id = pid;
PilotPic_find_offset = cftell(file);
return true;
}
// ----------------------------------------------------------
// PPic_FindClose
// Purpose:
// Stops a PPic_FindFirst/PPic_FindNext session
// ----------------------------------------------------------
void PPic_FindClose(void) {
PilotPic_find_offset = 0;
PilotPic_find_name[0] = 0;
}
// ----------------------------------------------------------
// PPic_GetPilot
// Purpose:
// Given a pilot id, it will return the pilot name of
// the pilot name. Returns false if it's an invalid pilot id.
// ----------------------------------------------------------
bool PPic_GetPilot(uint16_t pilot_id, char *pilot_name, int buffersize) {
if (!PilotPic_init)
return false;
int oldoffset = cftell(PilotPic_database_index_handle);
int offset = PPic_GetOffsetByID(pilot_id);
if (offset == -1)
return false;
// jump to the offset
// ------------------
cfseek(PilotPic_database_index_handle, offset, SEEK_SET);
if (cfeof(PilotPic_database_index_handle)) {
cfseek(PilotPic_database_index_handle, oldoffset, SEEK_SET);
return false;
}
// read in the pilot name
// ----------------------
uint8_t name_size;
name_size = cf_ReadByte(PilotPic_database_index_handle);
if (cfeof(PilotPic_database_index_handle)) {
cfseek(PilotPic_database_index_handle, oldoffset, SEEK_SET);
return false;
}
int toread = std::min<int>(name_size, buffersize - 1);
cf_ReadBytes((uint8_t *)pilot_name, toread, PilotPic_database_index_handle);
pilot_name[toread] = '\0';
cfseek(PilotPic_database_index_handle, oldoffset, SEEK_SET);
return true;
}
// ----------------------------------------------------------
// PPic_GetBitmapHandle
// Purpose:
// Given a pilot id, it will return a handle to the
// bitmap for the pilot. MAKE SURE YOU FREE THE BITMAP WITH
// bm_FreeBitmap(). Returns -1 if it was an illegal pilot id.
// Returns BAD_BITMAP_HANDLE if it couldn't open the bitmap.
// ----------------------------------------------------------
int PPic_GetBitmapHandle(uint16_t pilot_id) {
if (!PilotPic_init)
return -1;
int oldoffset = cftell(PilotPic_database_index_handle);
int offset = PPic_GetOffsetByID(pilot_id);
if (offset == -1)
return -1;
// jump to the offset
// ------------------
cfseek(PilotPic_database_index_handle, offset, SEEK_SET);
if (cfeof(PilotPic_database_index_handle)) {
cfseek(PilotPic_database_index_handle, oldoffset, SEEK_SET);
return -1;
}
// read in the pilot name
// ----------------------
uint8_t name_size;
char name_buffer[PILOT_STRING_SIZE];
name_size = cf_ReadByte(PilotPic_database_index_handle);
if (cfeof(PilotPic_database_index_handle)) {
cfseek(PilotPic_database_index_handle, oldoffset, SEEK_SET);
return -1;
}
cf_ReadBytes((uint8_t *)name_buffer, name_size, PilotPic_database_index_handle);
name_buffer[name_size] = '\0';
// next read in pilot_id
uint16_t pid;
pid = (uint16_t)cf_ReadShort(PilotPic_database_index_handle);
// next read past the bitmap name
uint8_t bmp_size;
char bitmap_name[_MAX_PATH];
bmp_size = cf_ReadByte(PilotPic_database_index_handle);
cf_ReadBytes((uint8_t *)bitmap_name, bmp_size, PilotPic_database_index_handle);
bitmap_name[bmp_size] = '\0';
cfseek(PilotPic_database_index_handle, oldoffset, SEEK_SET);
int bm_handle = bm_AllocLoadFileBitmap(IGNORE_TABLE(bitmap_name), 0);
if (bm_handle <= BAD_BITMAP_HANDLE)
return BAD_BITMAP_HANDLE;
return bm_handle;
}
// ===============================================================================
// this maps a pilot_id to an index file offset
struct tPilotPicIdOffset {
int offset;
uint16_t id;
};
tPilotPicIdOffset *Pilot_id_to_offset = NULL;
uint16_t *Sorted_Pilot_id_to_offset = NULL;
int PilotPic_count = 0;
// this maps the alphabet to an index file offset (first pilot_name that begins with the letter)
int Alphabet_to_offset[27];
// -----------------------------------------------
// PPic_BuildDatabase
// Purpose:
// Builds up some databases for fast access
// based off the index file.
// -----------------------------------------------
bool PPic_BuildDatabases(void) {
CFILE *file = PilotPic_database_index_handle;
if (!file)
return false;
// first rewind to the beginning and find out how many pilots we have
// -------------------------------------------------------------------
cf_Rewind(file);
PilotPic_count = cf_ReadInt(file);
if (PilotPic_count < 0) {
// hmm a negative!
mprintf(0, "PPIC: Invalid number of pilot pictures (%d)\n", PilotPic_count);
Int3();
PilotPic_count = 0;
cf_Rewind(file);
return false;
}
if (PilotPic_count > 65535) {
// too many!!!!
mprintf(0, "PPIC: Invalid number of pilot pictures (%d)\n", PilotPic_count);
Int3();
PilotPic_count = 0;
cf_Rewind(file);
return false;
}
// allocate all the memory we're going to need
// -------------------------------------------
Pilot_id_to_offset = (tPilotPicIdOffset *)mem_malloc(sizeof(tPilotPicIdOffset) * PilotPic_count);
Sorted_Pilot_id_to_offset = (uint16_t *)mem_malloc(sizeof(uint16_t) * PilotPic_count);
if (!Pilot_id_to_offset) {
// out of memory!!!
mprintf(0, "PPIC: Out of memory allocating index database\n");
Int3();
PilotPic_count = 0;
cf_Rewind(file);
return false;
}
// now it is time to go through the index file and build the databases
// -------------------------------------------------------------------
int count;
char name_buffer[256];
for (count = 0; count < 27; count++)
Alphabet_to_offset[count] = -1;
for (count = 0; count < PilotPic_count; count++) {
// get the current file position, we'll need it
int file_pos;
file_pos = cftell(file);
// first read in the pilot name
uint8_t name_size;
name_size = cf_ReadByte(file);
cf_ReadBytes((uint8_t *)name_buffer, name_size, file);
name_buffer[name_size] = '\0';
if (name_size >= PILOT_STRING_SIZE) {
mprintf(0, "PPIC: Too big: (%s)%d %d", name_buffer, count, name_size);
}
// next read in pilot_id
uint16_t pilot_id;
pilot_id = (uint16_t)cf_ReadShort(file);
// next read past the bitmap name
uint8_t bmp_size;
bmp_size = cf_ReadByte(file);
cfseek(file, bmp_size, SEEK_CUR);
// add the info to the database
Pilot_id_to_offset[count].offset = file_pos;
Pilot_id_to_offset[count].id = pilot_id;
char let_pos;
if (isalpha(name_buffer[0])) {
let_pos = toupper(name_buffer[0]) - 'A';
} else {
let_pos = 26; // not alpha letter
}
if (Alphabet_to_offset[let_pos] == -1) {
Alphabet_to_offset[let_pos] = file_pos;
}
}
// now sort the pilot ids
int i, t, j;
for (i = 0; i < PilotPic_count; i++) {
Sorted_Pilot_id_to_offset[i] = i;
}
for (i = 1; i <= PilotPic_count - 1; i++) {
t = Sorted_Pilot_id_to_offset[i];
// Shift elements down until
// insertion point found.
for (j = i - 1; j >= 0 && (Pilot_id_to_offset[Sorted_Pilot_id_to_offset[j]].id < Pilot_id_to_offset[t].id); j--) {
Sorted_Pilot_id_to_offset[j + 1] = Sorted_Pilot_id_to_offset[j];
}
// insert
Sorted_Pilot_id_to_offset[j + 1] = t;
}
cf_Rewind(file);
return true;
}
// -----------------------------------------------
// PPic_FreeDatabase
// Purpose:
// Frees up memory associated with the databases
// ------------------------------------------------
void PPic_FreeDatabase(void) {
for (int i = 0; i < 27; i++)
Alphabet_to_offset[i] = -1;
if (Pilot_id_to_offset) {
mem_free(Pilot_id_to_offset);
Pilot_id_to_offset = NULL;
}
if (Sorted_Pilot_id_to_offset) {
mem_free(Sorted_Pilot_id_to_offset);
Sorted_Pilot_id_to_offset = NULL;
}
PilotPic_count = 0;
}
// -------------------------------------------------
// PPic_JumpToPilot
// Purpose:
// Returns an offset into the file to where the
// the first pilot that matches pilot_name is. -1
// is returned if no match
// --------------------------------------------------
int PPic_JumpToPilot(char *pilot_name) {
int lett_offset;
int let_pos;
if (isalpha(pilot_name[0])) {
let_pos = toupper(pilot_name[0]) - 'A';
} else {
let_pos = 26;
}
lett_offset = Alphabet_to_offset[let_pos];
if (lett_offset == -1) {
// no pilot names begin with this letter
return -1;
}
CFILE *file = PilotPic_database_index_handle;
// jump to that position of the file
cfseek(file, lett_offset, SEEK_SET);
// now try to find the pilot
bool done = false;
int file_pos;
char name_buffer[PILOT_STRING_SIZE];
while (!done) {
// save the file position
file_pos = cftell(file);
// first read in the pilot name
uint8_t name_size;
name_size = cf_ReadByte(file);
if (cfeof(file)) {
done = true;
continue;
}
cf_ReadBytes((uint8_t *)name_buffer, name_size, file);
name_buffer[name_size] = '\0';
// next read in pilot_id
uint16_t pilot_id;
pilot_id = (uint16_t)cf_ReadShort(file);
// next read past the bitmap name
uint8_t bmp_size;
bmp_size = cf_ReadByte(file);
cfseek(file, bmp_size, SEEK_CUR);
int strcmpret = stricmp(name_buffer, pilot_name);
switch (strcmpret) {
case 0:
// we found a match
cfseek(file, file_pos, SEEK_SET);
return file_pos;
default:
if ((let_pos == 25) && !isalpha(name_buffer[0])) {
// we're searching the z's and moved to let_pos 26
return -1;
}
if (strcmpret > 0) {
// we can stop reading/searching
return -1;
}
}
if (cfeof(file))
done = true;
}
return -1;
}
// -------------------------------------
// PPic_GetOffsetByID
// Purpose:
// Returns the file offset of the given pilot id. -1 if not found
// -------------------------------------
int PPic_GetOffsetByID(uint16_t pilot_id) {
int index = PPic_GetIndexFromID(pilot_id);
if (index == -1)
return -1;
return Pilot_id_to_offset[index].offset;
}
// -------------------------------------
// PPic_GetIndexFromID
// Purpose:
// Returns the file index into the Pilot_id_to_offset
// list for the given id, -1 if not found.
// -------------------------------------
int PPic_GetIndexFromID(int id) {
// do a binary search for the id,return -1 if not found
int min = 0, max = PilotPic_count - 1;
int index_to_check;
int sort_index, sorted_val;
while (1) {
index_to_check = (min + max) / 2;
sort_index = Sorted_Pilot_id_to_offset[index_to_check];
sorted_val = Pilot_id_to_offset[sort_index].id;
if (sorted_val == id) {
// found it!
return sort_index;
}
if (min >= max) // exhausted search
break;
if (sorted_val > id) // search key after check key
min = index_to_check + 1;
else // search key before check key
max = index_to_check - 1;
}
return -1;
}