mirror of
https://github.com/lindbergh-loader/lindbergh-loader.git
synced 2026-10-05 05:08:17 +03:00
400 lines
11 KiB
C
400 lines
11 KiB
C
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/stat.h>
|
|
#include <unistd.h>
|
|
|
|
#include "cardReader.h"
|
|
#include "config.h"
|
|
#include "flowControl.h"
|
|
#include "log.h"
|
|
|
|
#define BLOCKSIZE 8
|
|
|
|
extern uint32_t gId;
|
|
extern int gGrp;
|
|
bool cardReaderInitialized = false;
|
|
|
|
void (*idWriteFileHeader)(void *) = NULL;
|
|
|
|
uint8_t cardHeader[] = {0x08, 0x00, 0x00, 0x00, 0x81, 0x00, 0x59, 0xda, 0x00, 0x00, 0x54, 0x4d, 0x50, 0x06, 0x03, 0x23, 0x10, 0x41, 0x62,
|
|
0xad, 0x00, 0x2b, 0x0b, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x95, 0x71, 0x52, 0x70, 0x00, 0x00,
|
|
0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfd, 0xd2};
|
|
|
|
SerialFD serialFD[2];
|
|
WriteCommand writeCmd[2];
|
|
ReadCommand readCmd[2];
|
|
|
|
void initReadCmd(int fdIdx)
|
|
{
|
|
readCmd[fdIdx].cmd = 0;
|
|
readCmd[fdIdx].cmdCompleted = false;
|
|
readCmd[fdIdx].cmdLen = 0;
|
|
readCmd[fdIdx].sentBytes = 0;
|
|
memset(&readCmd[fdIdx].sendBuffer, '\0', 256);
|
|
}
|
|
|
|
void initWriteCmd(int fdIdx)
|
|
{
|
|
writeCmd[fdIdx].cmdCompleted = false;
|
|
writeCmd[fdIdx].packetLen = 0;
|
|
writeCmd[fdIdx].recBytes = 0;
|
|
memset(&writeCmd[fdIdx].recBuffer, '\0', sizeof(uint8_t));
|
|
}
|
|
|
|
int initCardReader()
|
|
{
|
|
EmulatorConfig *config = getConfig();
|
|
|
|
if (gGrp != GROUP_VT3 && gGrp != GROUP_VT3_TEST && gId != R_TUNED)
|
|
{
|
|
log_warn("Warning: This Game does not support Card Reader, I will disable the card reader for you.\n");
|
|
config->emulateHW210CardReader = 0;
|
|
return 0;
|
|
}
|
|
|
|
if (config->emulateHW210CardReader && config->emulateDriveboard && gId != R_TUNED)
|
|
{
|
|
log_warn("Warning: This Game does not support Card Reader and Driver Board emulation enabled at the same time.\n");
|
|
log_warn("Please disable Drive Board emulations in the config file.\n");
|
|
return -1;
|
|
}
|
|
|
|
initReadCmd(0);
|
|
initReadCmd(1);
|
|
initWriteCmd(0);
|
|
initWriteCmd(1);
|
|
cardReaderInitialized = true;
|
|
return 0;
|
|
}
|
|
|
|
int carReaderGetFdIdx(int fd)
|
|
{
|
|
return (fd == serialFD[0].fd) ? 0 : 1;
|
|
}
|
|
|
|
uint8_t cardReaderGenChecksum(int fdIdx)
|
|
{
|
|
int checksum = 0;
|
|
for (int i = 0; i < readCmd[fdIdx].cmdLen - 1; i++)
|
|
{
|
|
checksum ^= readCmd[fdIdx].sendBuffer[i];
|
|
}
|
|
return checksum;
|
|
}
|
|
|
|
void buildOutputBuffer(int fdIdx, uint8_t cmd)
|
|
{
|
|
uint16_t packetLen = 0;
|
|
|
|
readCmd[fdIdx].sendBuffer[0] = 0x10;
|
|
readCmd[fdIdx].sendBuffer[1] = cmd;
|
|
switch (cmd)
|
|
{
|
|
case 0x10:
|
|
{
|
|
readCmd[fdIdx].sendBuffer[5] = 0x20;
|
|
}
|
|
case 0x11:
|
|
{
|
|
if (writeCmd[fdIdx].recBuffer[5] == 0x04)
|
|
{
|
|
initWriteCmd(fdIdx);
|
|
return;
|
|
}
|
|
}
|
|
case 0x14:
|
|
case 0x15:
|
|
case 0x20:
|
|
{
|
|
if (cmd == 0x20 && writeCmd[fdIdx].recBuffer[5] == 0x00)
|
|
readCmd[fdIdx].sendBuffer[4] = 0x80;
|
|
}
|
|
case 0x26:
|
|
case 0x27:
|
|
{
|
|
packetLen = 0x02;
|
|
readCmd[fdIdx].sendBuffer[3] = packetLen;
|
|
}
|
|
break;
|
|
case 0x21:
|
|
{
|
|
packetLen = 0x0a;
|
|
readCmd[fdIdx].sendBuffer[3] = packetLen;
|
|
readCmd[fdIdx].sendBuffer[6] = 0x81;
|
|
readCmd[fdIdx].sendBuffer[8] = 0x59;
|
|
readCmd[fdIdx].sendBuffer[9] = 0xda;
|
|
}
|
|
break;
|
|
case 0x22:
|
|
{
|
|
packetLen = 0x0a;
|
|
readCmd[fdIdx].sendBuffer[3] = packetLen;
|
|
readCmd[fdIdx].sendBuffer[7] = 0x08;
|
|
}
|
|
break;
|
|
case 0x33:
|
|
{
|
|
packetLen = 0x0a;
|
|
readCmd[fdIdx].sendBuffer[3] = packetLen;
|
|
readCmd[fdIdx].sendBuffer[6] = 0xff;
|
|
readCmd[fdIdx].sendBuffer[7] = 0xff;
|
|
}
|
|
break;
|
|
case 0x34:
|
|
{
|
|
uint16_t blockStart = (writeCmd[fdIdx].recBuffer[6] << 8) | writeCmd[fdIdx].recBuffer[7];
|
|
uint16_t blocksToRead = (writeCmd[fdIdx].recBuffer[8] << 8) | writeCmd[fdIdx].recBuffer[9];
|
|
packetLen = (BLOCKSIZE * blocksToRead) + 2;
|
|
readCmd[fdIdx].sendBuffer[3] = packetLen;
|
|
FILE *crdFile = fopen(serialFD[fdIdx].cardFilename, "rb");
|
|
fseek(crdFile, BLOCKSIZE * blockStart, SEEK_SET);
|
|
fread(readCmd[fdIdx].sendBuffer + 6, 1, packetLen - 2, crdFile);
|
|
fclose(crdFile);
|
|
}
|
|
break;
|
|
case 0x35:
|
|
{
|
|
readCmd[fdIdx].sendBuffer[3] = 0x02;
|
|
uint16_t blockStart = (writeCmd[fdIdx].recBuffer[6] << 8) | writeCmd[fdIdx].recBuffer[7];
|
|
uint16_t blocksToWrite = (writeCmd[fdIdx].recBuffer[8] << 8) | writeCmd[fdIdx].recBuffer[9];
|
|
packetLen = BLOCKSIZE * blocksToWrite;
|
|
FILE *crdFile = fopen(serialFD[fdIdx].cardFilename, "rb+");
|
|
fseek(crdFile, BLOCKSIZE * blockStart, SEEK_SET);
|
|
fwrite(writeCmd[fdIdx].recBuffer + 10, 1, packetLen, crdFile);
|
|
fclose(crdFile);
|
|
packetLen = 0x02;
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
log_error("Command not supported.\n");
|
|
}
|
|
}
|
|
readCmd[fdIdx].cmdLen = packetLen + 5;
|
|
readCmd[fdIdx].sendBuffer[readCmd[fdIdx].cmdLen - 1] = cardReaderGenChecksum(fdIdx);
|
|
}
|
|
|
|
size_t cardReaderWrite(int fd, const void *buf, size_t count)
|
|
{
|
|
if (!cardReaderInitialized)
|
|
return -1;
|
|
|
|
int fdIdx = carReaderGetFdIdx(fd);
|
|
if (writeCmd[fdIdx].cmdCompleted)
|
|
{
|
|
log_error("Something went wrong, a write command was issued after the command was completed.\n");
|
|
}
|
|
|
|
if (writeCmd[fdIdx].recBytes == 4)
|
|
{
|
|
writeCmd[fdIdx].packetLen = (writeCmd[fdIdx].recBuffer[2] << 8) | writeCmd[fdIdx].recBuffer[3];
|
|
}
|
|
|
|
writeCmd[fdIdx].recBuffer[writeCmd[fdIdx].recBytes] = *((u_int8_t *)buf);
|
|
writeCmd[fdIdx].recBytes++;
|
|
|
|
if (writeCmd[fdIdx].recBytes == writeCmd[fdIdx].packetLen + 5)
|
|
{
|
|
writeCmd[fdIdx].cmdCompleted = true;
|
|
initReadCmd(fdIdx);
|
|
buildOutputBuffer(fdIdx, writeCmd[fdIdx].recBuffer[1]);
|
|
}
|
|
return count;
|
|
}
|
|
|
|
size_t cardReaderRead(int fd, void *buf, size_t count)
|
|
{
|
|
if (!cardReaderInitialized)
|
|
return -1;
|
|
|
|
int fdIdx = carReaderGetFdIdx(fd);
|
|
if (!writeCmd[fdIdx].cmdCompleted || readCmd[fdIdx].cmdCompleted)
|
|
return -1;
|
|
|
|
if (readCmd[fdIdx].cmdLen == readCmd[fdIdx].sentBytes)
|
|
{
|
|
readCmd[fdIdx].cmdCompleted = true;
|
|
initWriteCmd(fdIdx);
|
|
return -1;
|
|
}
|
|
|
|
memcpy(buf, &readCmd[fdIdx].sendBuffer[readCmd[fdIdx].sentBytes], sizeof(uint8_t));
|
|
readCmd[fdIdx].sentBytes++;
|
|
return 1;
|
|
}
|
|
|
|
void cardReaderSetFd(int serial, int fd, char *fName)
|
|
{
|
|
serialFD[serial].fd = fd;
|
|
serialFD[serial].cardFilename = fName;
|
|
|
|
if (access(fName, F_OK) != 0)
|
|
{
|
|
FILE *crdFile = fopen(fName, "wb+");
|
|
if (getConfig()->crc32 == R_TUNED)
|
|
{
|
|
cardHeader[0x22] = 0x02;
|
|
cardHeader[0x23] = 0x60;
|
|
}
|
|
fwrite(cardHeader, 1, 56, crdFile);
|
|
uint8_t z = 0;
|
|
fseek(crdFile, 0x7c7, SEEK_CUR);
|
|
fwrite(&z, 1, 1, crdFile);
|
|
fclose(crdFile);
|
|
log_info("Cardfile %s created.\n", fName);
|
|
}
|
|
}
|
|
|
|
|
|
void idPatchEject()
|
|
{
|
|
switch (gId)
|
|
{
|
|
case INITIALD_4_EXP_REVB:
|
|
{
|
|
patchMemoryFromString(0x082939f9, "84"); // tickInsertWait
|
|
patchMemoryFromString(0x08293a2b, "85");
|
|
patchMemoryFromString(0x0819251e, "eb"); // reqRead
|
|
}
|
|
break;
|
|
case INITIALD_4_EXP_REVC:
|
|
{
|
|
patchMemoryFromString(0x08293aa9, "84"); // tickInsertWait
|
|
patchMemoryFromString(0x08293adb, "85");
|
|
patchMemoryFromString(0x081925ce, "eb"); // reqRead
|
|
}
|
|
break;
|
|
case INITIALD_4_EXP_REVD:
|
|
{
|
|
patchMemoryFromString(0x08295479, "84"); // tickInsertWait
|
|
patchMemoryFromString(0x082954ab, "85");
|
|
patchMemoryFromString(0x0819349e, "eb"); // reqRead
|
|
}
|
|
break;
|
|
case INITIALD_4_REVA:
|
|
{
|
|
patchMemoryFromString(0x0828149d, "85"); // tickInsertWait
|
|
patchMemoryFromString(0x08281603, "85");
|
|
patchMemoryFromString(0x0817943e, "eb"); // reqRead
|
|
}
|
|
break;
|
|
case INITIALD_4_REVB:
|
|
{
|
|
patchMemoryFromString(0x082815c5, "85"); // tickInsertWait
|
|
patchMemoryFromString(0x0828172b, "85");
|
|
patchMemoryFromString(0x081793ce, "eb"); // reqRead
|
|
}
|
|
break;
|
|
case INITIALD_4_REVC:
|
|
{
|
|
patchMemoryFromString(0x08281ae9, "85"); // tickInsertWait
|
|
patchMemoryFromString(0x08281c51, "85");
|
|
patchMemoryFromString(0x0817990c, "eb"); // reqRead
|
|
}
|
|
break;
|
|
case INITIALD_4_REVD:
|
|
{
|
|
patchMemoryFromString(0x082833dd, "85"); // tickInsertWait
|
|
patchMemoryFromString(0x08283543, "85");
|
|
patchMemoryFromString(0x08179c1c, "eb"); // reqRead
|
|
}
|
|
break;
|
|
case INITIALD_4_REVG:
|
|
{
|
|
patchMemoryFromString(0x082a4ae5, "84"); // tickInsertWait
|
|
patchMemoryFromString(0x082a4b17, "85");
|
|
patchMemoryFromString(0x08190cbe, "eb"); // reqRead
|
|
}
|
|
break;
|
|
case INITIALD_5_EXP:
|
|
{
|
|
patchMemoryFromString(0x083e1eb9, "84"); // tickInsertWait
|
|
patchMemoryFromString(0x083e1eeb, "85");
|
|
}
|
|
break;
|
|
case INITIALD_5_EXP_20:
|
|
{
|
|
patchMemoryFromString(0x083ebd1b, "84"); // tickInsertWait
|
|
patchMemoryFromString(0x083ebd51, "85");
|
|
}
|
|
break;
|
|
case INITIALD_5_EXP_20A:
|
|
{
|
|
patchMemoryFromString(0x083ec07b, "84"); // tickInsertWait
|
|
patchMemoryFromString(0x083ec0b1, "85");
|
|
}
|
|
break;
|
|
case INITIALD_5_JAP_REVA:
|
|
{
|
|
patchMemoryFromString(0x083d92bd, "84"); // tickInsertWait
|
|
patchMemoryFromString(0x083d92f1, "85");
|
|
}
|
|
break;
|
|
case INITIALD_5_JAP_REVF:
|
|
{
|
|
patchMemoryFromString(0x083ec6b9, "84"); // tickInsertWait
|
|
patchMemoryFromString(0x083ec6eb, "85");
|
|
}
|
|
}
|
|
}
|
|
|
|
void idWriteFileHeaderTram(void *param1)
|
|
{
|
|
idPatchEject();
|
|
idWriteFileHeader(param1);
|
|
}
|
|
|
|
bool idCardFileExists(const char *folderPath, long expectedSize, bool twoDigits)
|
|
{
|
|
if (folderPath != NULL && strlen(folderPath) > 0)
|
|
{
|
|
struct stat buffer;
|
|
stat(folderPath, &buffer);
|
|
if (!S_ISDIR(buffer.st_mode))
|
|
{
|
|
log_error("ID Card Folder does not exist! Check the config file.");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
char fileName[20];
|
|
char currentFullPath[1024];
|
|
char *fmt;
|
|
int maxCards = 999;
|
|
char *cardFilename = "InidCrd%03d.crd";
|
|
|
|
if (twoDigits)
|
|
{
|
|
maxCards = 99;
|
|
cardFilename = "InidCard%02d.crd";
|
|
}
|
|
|
|
for (int i = 0; i <= maxCards; ++i)
|
|
{
|
|
fmt = "%s%s";
|
|
sprintf(fileName, cardFilename, i);
|
|
if (folderPath != NULL && strlen(folderPath) > 0)
|
|
{
|
|
char lastChar = folderPath[strlen(folderPath) - 1];
|
|
if (lastChar != '/')
|
|
fmt = "%s/%s";
|
|
}
|
|
|
|
if (snprintf(currentFullPath, sizeof(currentFullPath), fmt, folderPath, fileName) >= sizeof(currentFullPath))
|
|
{
|
|
fprintf(stderr, "Error: Constructed path for '%s' in folder '%s' is too long.\n", fileName, folderPath);
|
|
continue;
|
|
}
|
|
|
|
struct stat fileStat;
|
|
if (stat(currentFullPath, &fileStat) == 0)
|
|
{
|
|
if (fileStat.st_size == expectedSize)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
} |