1
0
mirror of synced 2024-11-24 02:00:09 +01:00

Merge pull request #8 from bobbydilley/develop

Develop
This commit is contained in:
Bobby Dilley 2023-12-29 14:18:10 +00:00 committed by GitHub
commit c65006789f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 2165 additions and 291 deletions

22
.vscode/c_cpp_properties.json vendored Normal file
View File

@ -0,0 +1,22 @@
{
"configurations": [
{
"name": "Linux",
"includePath": [
"${default}"
],
"defines": [
"__USE_POSIX",
"__USE_MISC"
],
"cppStandard": "gnu++17",
"cStandard": "c99",
"intelliSenseMode": "linux-gcc-x86",
"forcedInclude": [
"${workspaceFolder}/../vscode-preinclude.h"
],
"compilerArgs": []
}
],
"version": 4
}

View File

@ -9,5 +9,8 @@
"ioctl.h": "c",
"segaeax.h": "c",
"passthrough.h": "c"
}
},
"C_Cpp.errorSquiggles": "disabled",
"C_Cpp.default.intelliSenseMode": "linux-gcc-x86",
"C_Cpp.default.compilerPath": "/usr/bin/gcc"
}

View File

@ -1,5 +1,5 @@
CC=gcc -m32
CFLAGS = -g -O0 -fPIC -m32 -D_GNU_SOURCE -Wall -Werror -Wno-unused-variable -Wno-unused-function
CFLAGS = -g -O0 -fPIC -m32 -Wall -Werror -Wno-unused-variable -Wno-unused-function
LD = g++ -m32
LDFLAGS = -Wl,-z,defs -rdynamic -static-libstdc++ -static-libgcc -lc -ldl -lGL -lglut -lX11 -lm -lpthread -shared -nostdlib
@ -7,7 +7,11 @@ BUILD = build
OBJS := $(patsubst %.c,%.o,$(wildcard src/lindbergh/*.c))
all: lindbergh.so libsegaapi.so libkswapapi.so
all: lindbergh lindbergh.so libsegaapi.so libkswapapi.so
lindbergh: src/lindbergh/lindbergh.c
mkdir -p $(BUILD)
$(CC) src/lindbergh/lindbergh.c -o $(BUILD)/lindbergh
lindbergh.so: $(OBJS)
mkdir -p $(BUILD)
@ -23,6 +27,10 @@ libsegaapi.so: src/libsegaapi/segaapi.o
libkswapapi.so: src/libkswapapi/libkswapapi.o
$(LIBSEGA_LD) $(LIBSEGA_LDFLAGS) src/libkswapapi/libkswapapi.o -L/usr/lib/i386-linux-gnu -fPIC -shared -o $(BUILD)/libkswapapi.so
rm -f src/libkswapapi/*.o
clean:
rm -rf $(BUILD)
rm -f src/lindbergh/*.o
rm -f src/libsegaapi/*.o
rm -f src/libkswapapi/*.o

View File

@ -1,16 +1,12 @@
# SEGA Lindbergh Emulator
This project aims to hook and emulate the various different parts of the SEGA Lindbergh allowing the games to run on modern versions of Linux.
This alpha stage project emulates the SEGA Lindbergh, allowing games to run on modern Linux computers with any NVIDIA graphics card.
You can view the supported titles [here.](docs/supported.md)
You will need an nvidia graphics card and I have tested with the latest version of Ubuntu.
Please be aware that the project is in very early stages, and there will be lots of issues with all games.
## Dependencies
First make sure you have up to date nVidia drivers for your computer, and then install the following:
First make sure you have up to date NVIDIA drivers and then install the following:
```
sudo dpkg --add-architecture i386
@ -32,7 +28,7 @@ sudo addgroup $USER dialout
sudo addgroup $USER input
```
To build, run the makefile, and then copy the contents of the build directory into your game directory and run.
To build, run the makefile, and then copy the contents of the build directory into the game directory and run.
```
make
@ -41,11 +37,13 @@ cd ~/the-house-of-the-dead-4/disk0/elf
LD_PRELOAD=lindbergh.so LD_LIBRARY_PATH=$LD_LIBRARY_PATH:. ./hod4M.elf
```
Some games will require extra libraries like `libposixtime.so`. These can be found in any dumps of the Lindbergh CF image.
Some games will require extra libraries like `libposixtime.so`, which can be found in dumps of the Lindbergh CF image.
A default configuration file is provided in `docs/lindbergh.conf`. It should be placed in the same folder as the game is run from. If no config file is present a default setting will be used.
A default configuration file is provided in `docs/lindbergh.conf`. It should be placed in the same folder the game is run from. If no config file is present a default setting will be used.
I recomend that you do not run this as root, and instead use the usergroups for input/dialout to give the emulator access to what it needs. The Lindbergh games expect full control of the Linux OS and with root privilages it is possible that they could cause damage to your computer.
Do not run this as root, instead use the usergroups for input/dialout to give the emulator access to what it needs. Lindbergh games expect full control of the Linux OS and with root privilages it is possible that they could cause damage to your computer.
A `lindbergh` executable is provided in the build directory to easily run the games. Place it in the same directory as the game elf, and run `./lindbergh` to automatically start the game with the correct environment variables set, or run `./lindbergh -t` for test mode.
## Controls
@ -62,5 +60,4 @@ Currently the controls are set up for The House of the Dead 4.
## Thanks
This project has been built by referencing things made by Teknoparrot, Doozer and JayFoxRox and from contributions by Rolel and
dkeruza-neo and extensive testing by Francesco so thanks to all of them!
This project has been built by referencing earlier projects by Teknoparrot and JayFoxRox and from contributions by Doozer, Rolel and dkeruza-neo with extensive testing by Francesco - thanks to all of them!

94
docs/games.txt Normal file
View File

@ -0,0 +1,94 @@
2SPICY
After Burner
After Burner Climax
After Burner Climax [Rev.A]
After Burner Climax [Rev.B]
After Burner Climax SDX
After Burner Climax SDX [Rev.A]
AMI-GYO
Ami No.3
Answer x Answer
Answer x Answer 2
Answer x Answer Live!
Blackjack Nailed Ace
Club Majesty - Extend - Cloud Nine
Club Majesty - Extend - Wheel Maniacs
Club Majesty Formal: Attractive Deck Poker
Club Majesty Formal: Cosmic Challenge
Club Majesty Formal: Cosmic Challenge [Rev.C]
Club Majesty: Gatling Poker
Club Majesty: Where's Wally!
Derby Owners Club 2008 Feel the Rush
Derby Owners Club 2009 Ride for the live
Ghost Squad Evolution
Harley Davidson King of the Road
HOTD4
HOTD4SP
HOTD EX
Hummer
Hummer Extreme
Hummer Extreme Edition MDX
Info Station 2 [Rev.C]
Initial D Arcade Stage 4
Initial D Arcade Stage 4 [Rev.A]
Initial D Arcade Stage 4 [Rev.B]
Initial D Arcade Stage 4 [Rev.C]
Initial D Arcade Stage 4 [Rev.D]
Initial D Arcade Stage 4 [Rev.G]
Initial D Arcade Stage 5
Initial D Arcade Stage 5 Exp 2.0
Initial D Arcade Stage 5 [Rev.A]
Let's Go Jungle
Let's Go Jungle Special
MJ4 Evolution
Outrun 2 SP SDX
Outrun 2 SP SDX [Rev.A]
Primeval Hunt
Rambo
Rambo (China)
Router Update (for VTF)
R-Tuned
Sangokushi Taisen 2
Sangokushi Taisen 3
Sega Network Casino Club STD
Sega Network Casino Club Ver.2
Sega Network Casino Club Ver.2 [Rev.B]
Sega Network Casino Club Ver.3
Sega Race TV
Sengokushi Taisen3 War Begins
Star Horse 2 Fifth Expansion [Rev.B]
Star Horse 2 Fifth Expansion [Rev.D]
Star Horse 2 Fifth Expansion [Rev.E]
Star Horse 2 Final Destination
Starhorse 2 Fourth Ambition
Star Horse 2 Fourth Ambition
Star Horse 2 New Generation
Star Horse 2 Second Fusion
Star Horse 2 Third Evolution
Star Horse 2 Third Evolution (Hong Kong)
Taisen Mahjong 4 (1.0)
Taisen Mahjong 4 (2.0)
Taisen Mahjong 4 (3.0)
Taisen Mahjong 4 (4.0)
Taisen Mahjong 4 (5.0)
Taisen Mahjong 4 (6.0)
Taisen Mahjong 4 (7.0)
Taisen Mahjong 4 (8.0)
toAmi-Gyo
to AMI GYO
VBIOS Update (2.0)
VBIOS Update (3.0)
VBIOS Update (for VTF)
Virtua Fighter 5
Virtua Fighter 5 Final Showdown
Virtua Fighter 5 Final Showdown [Rev.A]
Virtua Fighter 5 R
Virtua Fighter 5 [Rev.A]
Virtua Fighter 5 [Rev.B]
Virtua Fighter 5 [Rev.E]
Virtua Fighter 5 R [Rev.D]
Virtua Tennis 3
World Club Championship Football 2008-2009
World Club Championship Football 2009-2010 [ASIA]
World Club Championship Football 2009-2010 [JP]

View File

@ -1,7 +1,6 @@
# SEGA Lindbergh Emulator Configuration File
# Written by Bobby Dilley
# Set the colour of the lindbergh to change the Segaboot logo
# Possibly colours are: YELLOW and RED
LINDBERGH_COLOUR YELLOW
@ -22,6 +21,11 @@ WIDTH 640
# Set the requested dip switch height here
HEIGHT 480
# Set if the emulator should emulate JVS and use the keyboard/mouse for controls.
# If this is set to 0, then the emulator will route the traffic to the serial device
# defined in RIDEBOARD_PATH if it has been defined.
EMULATE_JVS 1
# Set if the emulator should emulate the rideboard used in the special games here
# If this is set to 0, then the emulator will route the traffic to the serial device
# defined in RIDEBOARD_PATH if it has been defined.
@ -37,22 +41,32 @@ EMULATE_DRIVEBOARD 0
# defined in MOTIONBOARD_PATH if it has been defined.
EMULATE_MOTIONBOARD 0
# Set if the emulator should emulate JVS and use the keyboard/mouse for controls.
# If this is set to 0, then the emulator will route the traffic to the serial device
# defined in RIDEBOARD_PATH if it has been defined.
EMULATE_JVS 1
# Define the path to pass the JVS packets to
# JVS_PATH /dev/ttyUSB0
JVS_PATH /dev/ttyUSB0
# Define the path to pass the rideboard packets to
# RIDEBOARD_PATH /dev/ttyUSB0
RIDEBOARD_PATH /dev/ttyUSB0
# Define the path to pass the driveboard packets to
# DRIVEBOARD_PATH /dev/ttyUSB0
DRIVEBOARD_PATH /dev/ttyUSB0
# Define the path to pass the motionboard packets to
# MOTIONBOARD_PATH /dev/ttyUSB0
MOTIONBOARD_PATH /dev/ttyUSB0
# Define the path to the sram.bin file
SRAM_PATH sram.bin
# Define the path to the eeprom.bin file
EEPROM_PATH eeprom.bin
# Set if the emulator should go full screen
FULLSCREEN 0
# Set the Region ( JP/US/EX )
REGION EX
# Set if you want the game to be Free Play
FREEPLAY 1
# Set if you want to see degug messages in the console
DEBUG_MSGS 0

View File

@ -10,7 +10,8 @@ Working is defined by getting into attract mode and running the game, but not ne
- After Burner Climax
- Ghost Squad Evolution
- Harley Davidson
- Let's Go Jungle Special (boots to ride error)
- Let's Go Jungle
- Let's Go Jungle Special
- Outrun 2 SP SDX
- R-Tuned
- Race TV
@ -19,20 +20,12 @@ Working is defined by getting into attract mode and running the game, but not ne
- The House Of The Dead 4 Special
- The House Of The Dead Ex
- Virtua Fighter 5
## Games that do not work or haven't been tested
- Hummer
- Initial D 4
- Initial D 5
- Let's Go Jungle
- Primevil
- Virtua Tennis 3
## How to run specific games
## Games that currently do not work
### Let's Go Jungle
- Hummer
- Initial D 5
- Primevil
No, this game will not start.
```LD_LIBRARY_PATH=$LD_LIBRARY_PATH:. LD_PRELOAD=lindbergh.so TEA_DIR=`pwd` ./lgj_final```

Binary file not shown.

View File

@ -4,6 +4,7 @@
#include <fcntl.h> /* File control definitions */
#include <errno.h> /* Error number definitions */
#include <stdlib.h> /* Standard library functions like malloc, free, exit, and atoi */
#include <sys/select.h>
#include "baseboard.h"
@ -59,6 +60,7 @@ uint8_t sharedMemory[1024 * 32] = {0};
int selectReply = -1;
int jvsFileDescriptor = -1;
int jvsPacketSize = -1;
int initBaseboard()
{
@ -191,7 +193,7 @@ int baseboardIoctl(int fd, unsigned int request, void *data)
if (getConfig()->emulateJVS)
{
processPacket();
processPacket(&jvsPacketSize);
}
else if (jvsFileDescriptor >= 0)
{
@ -250,9 +252,9 @@ int baseboardIoctl(int fd, unsigned int request, void *data)
{
if (getConfig()->emulateJVS)
{
memcpy(&sharedMemory[jvsCommand.destAddress], outputBuffer, outputPacket.length + 3);
memcpy(&sharedMemory[jvsCommand.destAddress], outputBuffer, jvsPacketSize);
_data[2] = jvsCommand.destAddress;
_data[3] = outputPacket.length + 3;
_data[3] = jvsPacketSize;
_data[1] = 1; // Set the status to success
}
else if (jvsFileDescriptor >= 0)

View File

@ -7,6 +7,8 @@
EmulatorConfig config = {0};
extern uint32_t elf_crc;
FILE *configFile = NULL;
#define CONFIG_PATH "lindbergh.conf"
@ -28,69 +30,187 @@ static char *getNextToken(char *buffer, char *seperator, char **saveptr)
return token;
}
static int detectGame()
static int detectGame(uint32_t elf_crc)
{
// For a better way of doing this, we should look for strings inside the game
// This will allow patching the game and for it to still be detected.
// strings %s | grep "RIDE TURN TEST" && strings %s | grep "PLEASE SHOOT GRID"
// shows you its hotd4S for example
if (strstr(program_invocation_name, "segaboot"))
if (elf_crc == 0x93ea7e11)
{
config.game = SEGABOOT;
config.game = SEGABOOT_2_4;
config.gameStatus = WORKING;
return 0;
}
if (strstr(program_invocation_name, "hod4"))
if (elf_crc == 0x3cc635ee)
{
config.game = SEGABOOT_2_4_SYM;
config.gameStatus = WORKING;
return 0;
}
if (elf_crc == 0xbc0c9ffa)
{
config.game = THE_HOUSE_OF_THE_DEAD_4;
config.gameStatus = WORKING;
return 0;
}
if (strstr(program_invocation_name, "Jennifer"))
if (elf_crc == 0x5df569f5)
{
config.game = OUTRUN;
config.game = THE_HOUSE_OF_THE_DEAD_4_STRIPPED;
config.gameStatus = WORKING;
return 0;
}
if (elf_crc == 0x7235bda8)
{
config.game = THE_HOUSE_OF_THE_DEAD_4_TEST;
config.gameStatus = WORKING;
return 0;
}
if (elf_crc == 0x12266f81)
{
config.game = THE_HOUSE_OF_THE_DEAD_4_SPECIAL;
config.gameStatus = WORKING;
return 0;
}
if (elf_crc == 0x83ba3b45)
{
config.game = THE_HOUSE_OF_THE_DEAD_4_SPECIAL_TEST;
config.gameStatus = WORKING;
return 0;
}
if (elf_crc == 0x85c0c22a)
{
config.game = THE_HOUSE_OF_THE_DEAD_EX;
config.gameStatus = WORKING;
return 0;
}
if (elf_crc == 0xb9a166bb)
{
config.game = THE_HOUSE_OF_THE_DEAD_EX_TEST;
config.gameStatus = WORKING;
return 0;
}
if (elf_crc == 0x6d055308)
{
config.game = OUTRUN_2_SP_SDX_REVA;
config.emulateDriveboard = 1;
config.emulateMotionboard = 1;
config.gameStatus = WORKING;
return 0;
}
if (strstr(program_invocation_name, "JenTest"))
if (elf_crc == 0xffdccaaa)
{
config.game = OUTRUN_TEST;
config.game = OUTRUN_2_SP_SDX_REVA_TEST;
config.emulateDriveboard = 1;
config.emulateMotionboard = 1;
config.gameStatus = WORKING;
return 0;
}
if (strstr(program_invocation_name, "lgj"))
if (elf_crc == 0xd4726d61)
{
config.game = LETS_GO_JUNGLE;
config.gameStatus = WORKING;
return 0;
}
if (strstr(program_invocation_name, "abc1080"))
if (elf_crc == 0xbbabc0e0)
{
config.game = ABC;
config.game = LETS_GO_JUNGLE_SPECIAL;
config.gameStatus = NOT_WORKING;
return 0;
}
if (strstr(program_invocation_name, "drive"))
if (elf_crc == 0xcc02de7d)
{
config.game = SRTV;
config.game = AFTER_BURNER_CLIMAX;
config.gameStatus = WORKING;
return 0;
}
if (strstr(program_invocation_name, "dsr"))
if (elf_crc == 0x152530dd)
{
config.game = RTUNED;
config.game = AFTER_BURNER_CLIMAX_REVA;
config.gameStatus = WORKING;
return 0;
}
if (strstr(program_invocation_name, "vf5"))
if (elf_crc == 0x4e9ccf33)
{
config.game = VF5;
config.game = INITIALD_4;
config.gameStatus = NOT_WORKING;
return 0;
}
if (elf_crc == 0x7f3f9f0c)
{
config.game = INITIALD_4_REVE;
config.gameStatus = NOT_WORKING;
return 0;
}
if (elf_crc == 0xfb096f81)
{
config.game = SEGA_RACE_TV;
config.emulateDriveboard = 1;
config.gameStatus = NOT_WORKING;
return 0;
}
if (elf_crc == 0x77ebac34)
{
config.game = RAMBO;
config.gameStatus = NOT_WORKING;
return 0;
}
if (elf_crc == 0xb05d9bbe)
{
config.game = R_TUNED;
config.emulateDriveboard = 1;
config.gameStatus = WORKING;
return 0;
}
if (elf_crc == 0x4c768eb4)
{
config.game = TOO_SPICY;
config.gameStatus = WORKING;
return 0;
}
if (elf_crc == 0xc4b7e89)
{
config.game = VIRTUA_TENNIS_3;
config.gameStatus = WORKING;
return 0;
}
if (elf_crc == 0xffe3b0fd)
{
config.game = VIRTUA_TENNIS_3_TEST;
config.gameStatus = NOT_WORKING;
return 0;
}
if (elf_crc == 0x1bf1b627)
{
config.game = VIRTUA_FIGHTER_5_REVC;
config.gameStatus = WORKING;
return 0;
}
if (elf_crc == 0x3CC635EE)
{
config.game = SEGABOOT_2_4;
config.gameStatus = WORKING;
return 0;
}
@ -100,32 +220,105 @@ static int detectGame()
char *getGameName()
{
char *unknownGameTitle = "Unknown Game";
switch (config.game)
{
case AFTER_BURNER_CLIMAX:
return "After Burner Climax";
case AFTER_BURNER_CLIMAX_REVA:
return "After Burner Climax Rev A";
case AFTER_BURNER_CLIMAX_REVB:
return "After Burner Climax Rev B";
case AFTER_BURNER_CLIMAX_SDX:
return "After Burner Climax SDX";
case AFTER_BURNER_CLIMAX_SDX_REVA:
return "After Burner Climax SDX Rev A";
case GHOST_SQUAD_EVOLUTION:
return "Ghost Squad Evolution";
case HARLEY_DAVIDSON:
return "Harley Davidson: King of the Road";
case HUMMER:
return "Hummer";
case HUMMER_EXTREME:
return "Hummer Extreme";
case HUMMER_EXTREME_MDX:
return "Hummer Extreme MDX";
case INITIALD_4:
return "Initial D Arcade Stage 4";
case INITIALD_5:
return "Initial D Arcade Stage 5";
case LETS_GO_JUNGLE:
return "Let's Go Jungle! Lost on the Island of Spice!";
case LETS_GO_JUNGLE_SPECIAL:
return "Let's Go Jungle! Special!";
case OUTRUN_2_SP_SDX:
return "Outrun 2 SP SDX";
case OUTRUN_2_SP_SDX_REVA:
return "Outrun 2 SP SDX Rev A";
case OUTRUN_2_SP_SDX_REVA_TEST:
return "Outrun 2 SP SDX Rev A Test Mode";
case OUTRUN_2_SP_SDX_TEST:
return "Outrun 2 SP SDX Test Mode";
case PRIMEVAL_HUNT:
return "Primeval Hunt";
case RAMBO:
return "Rambo";
case RAMBO_CHINA:
return "Rambo China Release";
case R_TUNED:
return "R-Tuned Ultimate Street Racing";
case SEGABOOT:
return "SEGABOOT";
return "Segaboot";
case SEGABOOT_2_4:
return "SEGABOOT 2.4";
case SEGABOOT_2_4_SYM:
return "SEGABOOT 2.4 with Symbols";
case SEGABOOT_2_6:
return "SEGABOOT 2.6";
case OUTRUN:
return "Outrun 2 SP";
case SEGA_RACE_TV:
return "SEGA Race TV";
case THE_HOUSE_OF_THE_DEAD_4:
return "The House of the Dead 4";
case LETS_GO_JUNGLE:
return "Let's Go Jungle! Lost on the Island of Spice";
case ABC:
return "After Burner Climax";
case SRTV:
return "SEGA Race TV";
case RTUNED:
return "R-Tuned Ultimate Street Racing";
case VF5:
case THE_HOUSE_OF_THE_DEAD_4_SPECIAL:
return "The House of the Dead 4 Special";
case THE_HOUSE_OF_THE_DEAD_4_SPECIAL_TEST:
return "The House of the Dead 4 Special Test Mode";
case THE_HOUSE_OF_THE_DEAD_4_TEST:
return "The House of the Dead 4 Test Mode";
case THE_HOUSE_OF_THE_DEAD_EX:
return "The House of the Dead Ex";
case TOO_SPICY:
return "2 Step : 2 Spicy";
case UNKNOWN:
return unknownGameTitle;
case VIRTUA_FIGHTER_5:
return "Virtua Fighter 5";
case VIRTUA_FIGHTER_5_FINAL_SHOWDOWN:
return "Virtua Fighter 5 Final Showdown";
case VIRTUA_FIGHTER_5_FINAL_SHOWDOWN_REVA:
return "Virtua Fighter 5 Final Showdown Rev A";
case VIRTUA_FIGHTER_5_R:
return " Virtua Fighter 5 R";
case VIRTUA_FIGHTER_5_REVA:
return "Virtua Fighter 5 Rev A";
case VIRTUA_FIGHTER_5_REVB:
return "Virtua Fighter 5 Rev B";
case VIRTUA_FIGHTER_5_REVC:
return "Virtua Fighter 5 Rev C";
case VIRTUA_FIGHTER_5_R_REVD:
return "Virtua Fighter 5 Rev D";
case VIRTUA_TENNIS_3:
return "Virtua Tennis 3";
case VIRTUA_TENNIS_3_TEST:
return "Virtua Tennis 3 Test Mode";
case THE_HOUSE_OF_THE_DEAD_4_STRIPPED:
return "The House of the Dead 4 Rev C";
case INITIALD_4_REVE:
return "Initial D 4 Exp Rev E";
default:
return "Unknown Game";
return unknownGameTitle;
}
return "Unknown Game";
return unknownGameTitle;
}
int readConfig(FILE *configFile, EmulatorConfig *config)
@ -172,6 +365,18 @@ int readConfig(FILE *configFile, EmulatorConfig *config)
else if (strcmp(command, "JVS_PATH") == 0)
strcpy(config->jvsPath, getNextToken(NULL, " ", &saveptr));
else if (strcmp(command, "RIDEBOARD_PATH") == 0)
strcpy(config->rideboardPath, getNextToken(NULL, " ", &saveptr));
else if (strcmp(command, "DRIVEBOARD_PATH") == 0)
strcpy(config->driveboardPath, getNextToken(NULL, " ", &saveptr));
else if (strcmp(command, "MOTIONBOARD_PATH") == 0)
strcpy(config->motionboardPath, getNextToken(NULL, " ", &saveptr));
else if (strcmp(command, "FREEPLAY") == 0)
config->freeplay = atoi(getNextToken(NULL, " ", &saveptr));
else if (strcmp(command, "LINDBERGH_COLOUR") == 0)
{
char colour[256];
@ -180,6 +385,21 @@ int readConfig(FILE *configFile, EmulatorConfig *config)
config->lindberghColour = RED;
}
else if (strcmp(command, "REGION") == 0)
{
char region[256];
strcpy(region, getNextToken(NULL, " ", &saveptr));
if (strcmp(region, "JP") == 0)
config->region = JP;
else if (strcmp(region, "US") == 0)
config->region = US;
else
config->region = EX;
}
else if (strcmp(command, "DEBUG_MSGS") == 0)
config->showDebugMessages = atoi(getNextToken(NULL, " ", &saveptr));
else
printf("Error: Unknown settings command %s\n", command);
}
@ -201,19 +421,22 @@ int initConfig()
strcpy(config.driveboardPath, "none");
strcpy(config.motionboardPath, "none");
strcpy(config.rideboardPath, "none");
config.width = 1024;
config.height = 768;
if (detectGame() != 0)
config.width = 640;
config.height = 480;
config.crc32 = elf_crc;
config.region = -1;
config.freeplay = -1;
config.showDebugMessages = 0;
if (detectGame(config.crc32) != 0)
{
printf("Warning: Unsure what game this is, using default configuration values\n");
printf("Warning: Unsure what game with CRC 0x%X is. Please submit this new game to the GitHub repository: https://github.com/bobbydilley/lindbergh-loader/issues/new?title=Please+add+new+game+0x%X&body=I+tried+to+launch+the+following+game:\n", config.crc32, config.crc32);
}
configFile = fopen(CONFIG_PATH, "r");
if (configFile == NULL)
{
printf("Warning: Cannot open %s, using default values\n", CONFIG_PATH);
printf("Warning: Cannot open %s, using default values.\n", CONFIG_PATH);
return 1;
}

View File

@ -4,18 +4,53 @@
typedef enum
{
UNKNOWN,
AFTER_BURNER_CLIMAX,
AFTER_BURNER_CLIMAX_REVA,
AFTER_BURNER_CLIMAX_REVB,
AFTER_BURNER_CLIMAX_SDX,
AFTER_BURNER_CLIMAX_SDX_REVA,
GHOST_SQUAD_EVOLUTION,
HARLEY_DAVIDSON,
HUMMER,
HUMMER_EXTREME,
HUMMER_EXTREME_MDX,
INITIALD_4,
INITIALD_4_REVE,
INITIALD_5,
LETS_GO_JUNGLE,
LETS_GO_JUNGLE_SPECIAL,
OUTRUN_2_SP_SDX,
OUTRUN_2_SP_SDX_REVA,
OUTRUN_2_SP_SDX_REVA_TEST,
OUTRUN_2_SP_SDX_TEST,
PRIMEVAL_HUNT,
RAMBO,
RAMBO_CHINA,
R_TUNED,
SEGABOOT,
SEGABOOT_2_4,
SEGABOOT_2_4_SYM,
SEGABOOT_2_6,
SEGA_RACE_TV,
THE_HOUSE_OF_THE_DEAD_4,
OUTRUN,
OUTRUN_TEST,
LETS_GO_JUNGLE,
ABC,
SRTV,
RTUNED,
VF5
THE_HOUSE_OF_THE_DEAD_4_STRIPPED,
THE_HOUSE_OF_THE_DEAD_4_SPECIAL,
THE_HOUSE_OF_THE_DEAD_4_SPECIAL_TEST,
THE_HOUSE_OF_THE_DEAD_4_TEST,
THE_HOUSE_OF_THE_DEAD_EX,
THE_HOUSE_OF_THE_DEAD_EX_TEST,
TOO_SPICY,
UNKNOWN,
VIRTUA_FIGHTER_5,
VIRTUA_FIGHTER_5_FINAL_SHOWDOWN,
VIRTUA_FIGHTER_5_FINAL_SHOWDOWN_REVA,
VIRTUA_FIGHTER_5_R,
VIRTUA_FIGHTER_5_REVA,
VIRTUA_FIGHTER_5_REVB,
VIRTUA_FIGHTER_5_REVC,
VIRTUA_FIGHTER_5_R_REVD,
VIRTUA_TENNIS_3,
VIRTUA_TENNIS_3_TEST
} Game;
typedef enum
@ -24,6 +59,19 @@ typedef enum
RED
} Colour;
typedef enum
{
WORKING,
NOT_WORKING
} GameStatus;
typedef enum
{
JP,
US,
EX
} GameRegion;
typedef struct
{
int emulateRideboard;
@ -41,6 +89,11 @@ typedef struct
int height;
Game game;
Colour lindberghColour;
GameStatus gameStatus;
uint32_t crc32;
GameRegion region;
int freeplay;
int showDebugMessages;
} EmulatorConfig;
int initConfig();

View File

@ -3,9 +3,10 @@
#include <stdint.h>
#include "eeprom.h"
#include "eeprom_settings.h"
#include "config.h"
#define I2C_SMBUS_BLOCK_MAX 32
#define I2C_SMBUS_BLOCK_MAX 32
#define I2C_GET_FUNCTIONS 0x705
#define I2C_SMBUS_TRANSFER 0x720
#define I2C_SET_SLAVE_MODE 0x703
@ -14,17 +15,19 @@
#define I2C_SEEK 2
#define I2C_WRITE 3
union i2c_smbus_data {
uint8_t byte;
uint16_t word;
uint8_t block[I2C_SMBUS_BLOCK_MAX + 2];
union i2c_smbus_data
{
uint8_t byte;
uint16_t word;
uint8_t block[I2C_SMBUS_BLOCK_MAX + 2];
};
struct i2c_smbus_ioctl_data {
uint8_t read_write;
uint8_t command;
uint32_t size;
union i2c_smbus_data *data;
struct i2c_smbus_ioctl_data
{
uint8_t read_write;
uint8_t command;
uint32_t size;
union i2c_smbus_data *data;
};
FILE *eeprom = NULL;
@ -46,6 +49,35 @@ int initEeprom()
eeprom = fopen(eepromPath, "rb+");
if (eepromSettingsInit(eeprom) != 0)
{
printf("Error initializing eeprom settings.");
fclose(eeprom);
return 1;
}
if (getConfig()->region != -1)
{
if (getRegion() != getConfig()->region)
setRegion(eeprom, getConfig()->region);
}
if (getConfig()->freeplay != -1)
{
if (getFreeplay() != getConfig()->freeplay)
setFreeplay(eeprom, getConfig()->freeplay);
}
if ((getConfig()->game == LETS_GO_JUNGLE_SPECIAL) || (getConfig()->game == THE_HOUSE_OF_THE_DEAD_EX) || (getConfig()->game == THE_HOUSE_OF_THE_DEAD_4_SPECIAL))
{
if (fixCreditSection(eeprom) != 0)
{
printf("Error initializing eeprom settings.");
fclose(eeprom);
return 1;
}
}
fseek(eeprom, 0, SEEK_SET);
return 0;

View File

@ -0,0 +1,345 @@
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "config.h"
#include "eeprom_settings.h"
uint32_t crc32_table[255];
typedef struct
{
uint16_t offset;
uint16_t size;
} eepromOffsets;
typedef enum
{
STATIC,
NETWORK_TYPE,
ETH0,
ETH1,
CREDIT,
BACKUP
} eepromSection;
eepromOffsets eepromOffsetTable[] = {
{0x0000, 0x0020}, // amSysDataRecord_Static
{0x00A0, 0x0010}, // amSysDataRecord_NetworkType
{0x00B0, 0x0020}, // amSysDataRecord_Eth0
{0x00D0, 0x0020}, // amSysDataRecord_Eth1
{0x0100, 0x0040}, // amSysDataRecord_Credit
{0x0400, 0x0060}}; // amSysDataRecord_Backup
unsigned char eepromBuffer[512];
void build_crc32_table()
{
for (uint32_t i = 0; i < 256; i++)
{
uint32_t ch = i;
uint32_t crc = 0;
for (size_t j = 0; j < 8; j++)
{
uint32_t b = (ch ^ crc) & 1;
crc >>= 1;
if (b)
crc = crc ^ 0xEDB88320;
ch >>= 1;
}
crc32_table[i] = crc;
}
}
uint32_t gen_crc(int section, size_t n)
{
unsigned char *buff = &eepromBuffer[eepromOffsetTable[section].offset + 4];
unsigned long crc = 0xfffffffful;
size_t i;
for (i = 0; i < n; i++)
crc = crc32_table[*buff++ ^ (crc & 0xff)] ^ (crc >> 8);
return (crc);
}
void addCRCtoBuffer(int section)
{
uint32_t crc = gen_crc(section, eepromOffsetTable[section].size - 4);
memcpy(&eepromBuffer[eepromOffsetTable[section].offset], &crc, sizeof(uint32_t));
}
int fillBuffer(FILE *file)
{
fseek(file, 0, SEEK_SET);
int b = fread(eepromBuffer, 1, 512, file);
if (b < 512)
return 1;
return 0;
}
int checkCRCinBuffer(int section)
{
uint32_t crc;
memcpy(&crc, &eepromBuffer[eepromOffsetTable[section].offset], 4);
if (crc != gen_crc(section, eepromOffsetTable[section].size - 4))
return 1;
return 0;
}
int createStaticSection()
{
unsigned char *buff = &eepromBuffer[eepromOffsetTable[STATIC].offset];
memset(buff, 0, eepromOffsetTable[STATIC].size);
buff[14] = 0;
memcpy(buff + 15, "AAGX-01A00009999", 16);
return 0;
}
int createNetworkTypeSection()
{
memset(&eepromBuffer[eepromOffsetTable[NETWORK_TYPE].offset], 0,
eepromOffsetTable[NETWORK_TYPE].size);
return 0;
}
int createEthSection(int section)
{
unsigned char *buff = &eepromBuffer[eepromOffsetTable[section].offset];
memset(buff, 0, eepromOffsetTable[section].size);
uint32_t value = 1;
memcpy(buff + 8, &value, sizeof(uint32_t));
value = inet_addr("10.0.0.1");
if (section == ETH1)
value += (1 << 24);
memcpy(buff + 12, &value, sizeof(uint32_t));
value = inet_addr("255.255.255.0");
memcpy(buff + 16, &value, sizeof(uint32_t));
value = inet_addr("0.0.0.0");
memcpy(buff + 20, &value, sizeof(uint32_t));
memcpy(buff + 24, &value, sizeof(uint32_t));
memcpy(buff + 28, &value, sizeof(uint32_t));
return 0;
}
int createCreditSection()
{
unsigned char *buff = &eepromBuffer[eepromOffsetTable[CREDIT].offset];
memset(buff, 0, eepromOffsetTable[CREDIT].size);
buff[32] = 99;
buff[33] = 9;
buff[34] = 4;
buff[35] = 0; // Coin chute type [COMMON (Default) / INDIVIDUAL]
buff[36] = 1; // Service Type [COMMON / INDIVIDUAL (Default)]
buff[38] = 1; //
buff[39] = 0; // Freeplay set to 1
buff[40] = 1; // Credits Chute #1
buff[41] = 1; // Credits Chute #1
buff[42] = 0; //
buff[43] = 1; // Coins
for (size_t i = 0; i < 8; i++)
{
buff[44 + i] = 1;
}
return 0;
}
int writeSectiontoFile(FILE *file, int section)
{
unsigned char *buff = &eepromBuffer[eepromOffsetTable[section].offset];
// Original
if (fseek(file, eepromOffsetTable[section].offset, SEEK_SET) != 0)
return 1;
if (fwrite(buff, eepromOffsetTable[section].size, 1, file) != 1)
return 1;
// Duplicate
if (fseek(file, eepromOffsetTable[section].offset + 0x200, SEEK_SET) != 0)
return 1;
if (fwrite(buff, eepromOffsetTable[section].size, 1, file) != 1)
return 1;
return 0;
}
int getRegion()
{
return eepromBuffer[14];
}
int setRegion(FILE *eeprom, int region)
{
eepromBuffer[14] = region;
addCRCtoBuffer(STATIC);
if (writeSectiontoFile(eeprom, STATIC) != 0)
{
printf("Error writing to eeprom.");
return 1;
}
return 0;
}
int getFreeplay()
{
return eepromBuffer[eepromOffsetTable[CREDIT].offset + 39];
}
int setFreeplay(FILE *eeprom, int freeplay)
{
if (createCreditSection() != 0)
{
printf("Error setting Free Play.");
return 1;
}
eepromBuffer[eepromOffsetTable[CREDIT].offset + 39] = freeplay;
addCRCtoBuffer(CREDIT);
if (writeSectiontoFile(eeprom, CREDIT) != 0)
{
printf("Error writing to eeprom.");
return 1;
}
return 0;
}
int fixCreditSection(FILE *eeprom)
{
eepromBuffer[eepromOffsetTable[CREDIT].offset + 36] = 0;
eepromBuffer[eepromOffsetTable[CREDIT].offset + 39] = 0;
addCRCtoBuffer(CREDIT);
if (writeSectiontoFile(eeprom, CREDIT) != 0)
{
printf("Error writing to eeprom.");
return 1;
}
return 0;
}
int eepromSettingsInit( FILE *eeprom)
{
build_crc32_table();
eeprom = fopen("eeprom.bin", "r+b");
if (eeprom == NULL)
{
printf("eeprom.bin cannot be opened, let's create a new one.\n");
eeprom = fopen("eeprom.bin", "w+b");
}
fseek(eeprom, 0, SEEK_END);
int size = ftell(eeprom);
fseek(eeprom, 0, SEEK_SET);
if (size >= 832) // eeprom initialized at least by SEGABOOT
{
fillBuffer(eeprom); // Fills buffer with 1st 512 bytes of the eeprom file
if (checkCRCinBuffer(STATIC) != 0)
{
// Create section from scratch
if (createStaticSection() != 0)
{
printf("Error creating Static Section.\n");
return 1;
}
else
{
addCRCtoBuffer(STATIC);
if (writeSectiontoFile(eeprom, STATIC) != 0)
{
printf("Error writing to eeprom.bin [STATIC].");
return 1;
}
}
}
if (checkCRCinBuffer(NETWORK_TYPE) != 0)
{
// Create section from scratch
if (createNetworkTypeSection() != 0)
{
printf("Error creating NetworkType Section.\n");
return 1;
}
else
{
addCRCtoBuffer(NETWORK_TYPE);
if (writeSectiontoFile(eeprom, NETWORK_TYPE) != 0)
{
printf("Error writing to eeprom.bin [NETWORK_TYPE].");
return 1;
}
}
}
if (checkCRCinBuffer(ETH0) != 0)
{
// Create section from scratch
if (createEthSection(ETH0) != 0)
{
printf("Error creating Eth0 Section.\n");
return 1;
}
else
{
addCRCtoBuffer(ETH0);
if (writeSectiontoFile(eeprom, ETH0) != 0)
{
printf("Error writing to eeprom.bin [ETH0].");
return 1;
}
}
}
if (checkCRCinBuffer(ETH1) != 0)
{
// Create section from scratch
if (createEthSection(ETH1) != 0)
{
printf("Error creating Eth0 Section.\n");
return 1;
}
else
{
addCRCtoBuffer(ETH1);
if (writeSectiontoFile(eeprom, ETH1) != 0)
{
printf("Error writing to eeprom.bin [ETH1].");
return 1;
}
}
}
if (checkCRCinBuffer(CREDIT) != 0)
{
// Create section from scratch
if (createCreditSection() != 0)
{
printf("Error creating CREDIT Section.\n");
return 1;
}
else
{
addCRCtoBuffer(CREDIT);
if (writeSectiontoFile(eeprom, NETWORK_TYPE) != 0)
{
printf("Error writing to eeprom.bin [CREDIT]].");
return 1;
}
}
}
}
else
{
// Create all sections from scratch
if ((createStaticSection() != 0) || (createNetworkTypeSection() != 0) ||
(createEthSection(ETH0) != 0) || (createEthSection(ETH1) != 0) || (createCreditSection() != 0))
{
printf("Error creating section from scratch.\n");
return 1;
}
addCRCtoBuffer(STATIC);
addCRCtoBuffer(NETWORK_TYPE);
addCRCtoBuffer(ETH0);
addCRCtoBuffer(ETH1);
addCRCtoBuffer(CREDIT);
if ((writeSectiontoFile(eeprom, STATIC) != 0) || (writeSectiontoFile(eeprom, NETWORK_TYPE) != 0) ||
(writeSectiontoFile(eeprom, ETH0) != 0) || (writeSectiontoFile(eeprom, ETH1) != 0) ||
(writeSectiontoFile(eeprom, CREDIT) != 0))
{
printf("Error writing section from scratch.\n");
return 1;
}
}
return 0;
}

View File

@ -0,0 +1,6 @@
int eepromSettingsInit(FILE *eeprom);
int getRegion();
int getFreeplay();
int setRegion(FILE *eeprom, int region);
int setFreeplay(FILE *eeprom, int freeplay);
int fixCreditSection(FILE *eeprom);

View File

@ -1,3 +1,4 @@
#define _GNU_SOURCE
#include <GL/freeglut.h>
#include <GL/glx.h>
#include <X11/extensions/xf86vmode.h>
@ -25,11 +26,11 @@ FGAPI int FGAPIENTRY glutEnterGameMode()
{
char gameTitle[256] = {0};
strcat(gameTitle, getGameName());
strcat(gameTitle, " (GLUT)");
glutCreateWindow(gameTitle);
// Outrun doesn't run the glutMainLoop through, so we'll do that here
if (getConfig()->game == OUTRUN || getConfig()->game == OUTRUN_TEST)
Game game = getConfig()->game;
if (game == OUTRUN_2_SP_SDX || game == OUTRUN_2_SP_SDX_TEST || game == OUTRUN_2_SP_SDX_REVA || game == OUTRUN_2_SP_SDX_REVA_TEST)
{
pthread_t glutMainLoopID;
pthread_create(&glutMainLoopID, NULL, &glutMainLoopThread, NULL);
@ -51,7 +52,7 @@ FGAPI void FGAPIENTRY glutSetCursor(int cursor)
FGAPI void FGAPIENTRY glutGameModeString(const char *string)
{
printf("glutGameModeString: %s\n", string);
// printf("glutGameModeString: %s\n", string);
char gameModeString[1024];
strcpy(gameModeString, string);
@ -85,6 +86,14 @@ FGAPI void FGAPIENTRY glutGameModeString(const char *string)
}
}
/**
* Stop the house of the dead games turning keyboard repeating off.
*/
int XAutoRepeatOff(Display *display)
{
return 0;
}
Window XCreateWindow(Display *display, Window parent, int x, int y, unsigned int width, unsigned int height, unsigned int border_width, int depth, unsigned int class, Visual *visual, unsigned long valueMask, XSetWindowAttributes *attributes)
{
@ -98,7 +107,7 @@ Window XCreateWindow(Display *display, Window parent, int x, int y, unsigned int
// attributes->override_redirect = False;
Window window = _XCreateWindow(display, parent, x, y, width, height, border_width, depth, class, visual, valueMask, attributes);
printf("XCreateWindow Resolution %d %d %d %d\n", x, y, width, height);
printf("The resolution is %dx%d \n", width, height);
if (getConfig()->fullscreen)
{
@ -136,120 +145,40 @@ int XStoreName(Display *display, Window w, const char *window_name)
int (*_XStoreName)(Display *display, Window w, const char *window_name) = dlsym(RTLD_NEXT, "XStoreName");
char gameTitle[256] = {0};
strcat(gameTitle, getGameName());
strcat(gameTitle, " (X11)");
return _XStoreName(display, w, gameTitle);
}
int XNextEvent(Display *display, XEvent *event_return)
{
int (*_XNextEvent)(Display *display, XEvent *event_return) = dlsym(RTLD_NEXT, "XNextEvent");
int returnValue = _XNextEvent(display, event_return);
// Return now if we're not emulating JVS
if (!getConfig()->emulateJVS)
{
return returnValue;
}
switch (event_return->type)
{
case KeyRelease:
case KeyPress:
{
switch (event_return->xkey.keycode)
{
case 28:
setSwitch(SYSTEM, BUTTON_TEST, event_return->type == KeyPress);
break;
case 39:
setSwitch(PLAYER_1, BUTTON_SERVICE, event_return->type == KeyPress);
break;
case 14:
incrementCoin(PLAYER_1, event_return->type == KeyPress);
break;
case 15:
incrementCoin(PLAYER_2, event_return->type == KeyPress);
break;
case 111:
setSwitch(PLAYER_1, BUTTON_UP, event_return->type == KeyPress);
break;
case 116:
setSwitch(PLAYER_1, BUTTON_DOWN, event_return->type == KeyPress);
break;
case 113:
setSwitch(PLAYER_1, BUTTON_LEFT, event_return->type == KeyPress);
break;
case 114:
setSwitch(PLAYER_1, BUTTON_RIGHT, event_return->type == KeyPress);
break;
case 10:
setSwitch(PLAYER_1, BUTTON_START, event_return->type == KeyPress);
break;
case 24:
setSwitch(PLAYER_1, BUTTON_1, event_return->type == KeyPress);
break;
case 25:
setSwitch(PLAYER_1, BUTTON_2, event_return->type == KeyPress);
break;
case 26:
setSwitch(PLAYER_1, BUTTON_3, event_return->type == KeyPress);
break;
case 27:
setSwitch(PLAYER_1, BUTTON_4, event_return->type == KeyPress);
break;
default:
break;
}
}
break;
case MotionNotify:
{
setAnalogue(ANALOGUE_1, ((double)event_return->xmotion.x / (double)getConfig()->width) * 255);
setAnalogue(ANALOGUE_2, ((double)event_return->xmotion.y / (double)getConfig()->height) * 255);
}
break;
case ButtonPress:
case ButtonRelease:
{
switch (event_return->xbutton.button)
{
case 1: // Trigger
setSwitch(PLAYER_1, BUTTON_1, event_return->type == ButtonPress);
break;
case 3: // Reload
setSwitch(PLAYER_1, BUTTON_2, event_return->type == ButtonPress);
break;
case 9: // Gun Button
setSwitch(PLAYER_1, BUTTON_3, event_return->type == ButtonPress);
break;
default:
break;
}
}
break;
}
return returnValue;
}
int XSetStandardProperties(Display *display, Window window, const char *window_name, const char *icon_name, Pixmap icon_pixmap, char **argv, int argc, XSizeHints *hints)
{
int (*_XSetStandardProperties)(Display *display, Window window, const char *window_name, const char *icon_name, Pixmap icon_pixmap, char **argv, int argc, XSizeHints *hints) = dlsym(RTLD_NEXT, "XSetStandardProperties");
char gameTitle[256] = {0};
strcat(gameTitle, getGameName());
strcat(gameTitle, " (X11)");
return _XSetStandardProperties(display, window, gameTitle, icon_name, icon_pixmap, argv, argc, hints);
}
Bool XF86VidModeSwitchToMode(Display *display, int screen, XF86VidModeModeInfo *modeline)
Bool XF86VidModeSwitchToMode(Display *display, int screen, XF86VidModeModeInfo *modesinfo)
{
return 0;
}
int XF86VidModeGetAllModeLines(Display *display, int screen, int *modecount_return, XF86VidModeModeInfo ***modesinfo)
{
int (*_XF86VidModeGetAllModeLines)(Display *display, int screen, int *modecount_return, XF86VidModeModeInfo ***modesinfo) = dlsym(RTLD_NEXT, "XF86VidModeGetAllModeLines");
if (_XF86VidModeGetAllModeLines(display, screen, modecount_return, modesinfo) != 1)
{
printf("Error: Could not get list of screen modes.\n");
exit(1);
}
else
{
XF86VidModeModeInfo **modes = *modesinfo;
modes[0]->hdisplay = getConfig()->width;
modes[0]->vdisplay = getConfig()->height;
}
return true;
}
typedef unsigned int uint;
int glXSwapIntervalSGI(int interval)

235
src/lindbergh/hook.c Normal file → Executable file
View File

@ -1,3 +1,5 @@
#define _GNU_SOURCE
#include <link.h>
#include <dlfcn.h>
#include <errno.h>
#include <linux/sockios.h>
@ -13,6 +15,8 @@
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <cpuid.h>
#include <unistd.h>
#include "hook.h"
@ -24,6 +28,8 @@
#include "rideboard.h"
#include "securityboard.h"
#include "patch.h"
#include "pcidata.h"
#include "input.h"
#define HOOK_FILE_NAME "/dev/zero"
@ -34,23 +40,38 @@
#define CPUINFO 0
#define OSRELEASE 1
#define PCI_CARD_1F0 2
int hooks[5] = {-1, -1, -1, -1};
FILE *fileHooks[2] = {NULL, NULL};
int fileRead[2] = {0, 0};
FILE *fileHooks[3] = {NULL, NULL, NULL};
int fileRead[3] = {0, 0, 0};
char envpath[100];
uint32_t elf_crc = 0;
cpuvendor cpu_vendor = {0};
static int callback(struct dl_phdr_info *info, size_t size, void *data);
uint16_t basePortAddress = 0xFFFF;
/**
* Signal handler for the SIGSEGV signal, which is triggered when a process tries to access an illegal memory location.
* @param signal
* @param info
* @param ptr
*/
static void handleSegfault(int signal, siginfo_t *info, void *ptr)
{
ucontext_t *ctx = ptr;
// Get the address of the instruction causing the segfault
uint8_t *code = (uint8_t *)ctx->uc_mcontext.gregs[REG_EIP];
switch (*code)
{
case 0xED:
{
// Get the port number from the EDX register
uint16_t port = ctx->uc_mcontext.gregs[REG_EDX] & 0xFFFF;
// The first port called is usually random, but everything after that
@ -60,9 +81,11 @@ static void handleSegfault(int signal, siginfo_t *info, void *ptr)
if (basePortAddress == 0xFFFF)
basePortAddress = port;
// Adjust the port number if necessary
if (port > 0x38)
port = port - basePortAddress;
// Call the security board input function with the port number and data
securityBoardIn(port, (uint32_t *)&(ctx->uc_mcontext.gregs[REG_EAX]));
ctx->uc_mcontext.gregs[REG_EIP]++;
@ -70,15 +93,17 @@ static void handleSegfault(int signal, siginfo_t *info, void *ptr)
}
break;
case 0xE7: // OUT IMMIDIATE
case 0xE7: // OUT IMMEDIATE
{
// Increment the instruction pointer by two to skip over this instruction
ctx->uc_mcontext.gregs[REG_EIP] += 2;
return;
}
break;
case 0xE6: // OUT IMMIDIATE
case 0xE6: // OUT IMMEDIATE
{
// Increment the instruction pointer by two to skip over this instruction
ctx->uc_mcontext.gregs[REG_EIP] += 2;
return;
}
@ -110,7 +135,13 @@ static void handleSegfault(int signal, siginfo_t *info, void *ptr)
void __attribute__((constructor)) hook_init()
{
printf("SEGA Lindbergh Loader\nRobert Dilley 2022\nNot for public consumption\n\n");
printf("SEGA Lindbergh Loader\nRobert Dilley 2023\nNot for public consumption\n\n");
// Get offsets of the Game's ELF and calculate CRC32.
dl_iterate_phdr(callback, NULL);
// Get CPU ID
getCPUID();
// Implement SIGSEGV handler
struct sigaction act;
@ -147,9 +178,20 @@ void __attribute__((constructor)) hook_init()
exit(1);
}
if (initInput() != 0)
exit(1);
securityBoardSetDipResolution(getConfig()->width, getConfig()->height);
printf("Now emulating %s\n", getGameName());
printf("Now starting \"%s\"", getGameName());
if (getConfig()->gameStatus == WORKING)
{
printf((", this game is working.\n"));
}
else
{
printf((", this game is NOT working.\n"));
}
}
int open(const char *pathname, int flags)
@ -161,14 +203,12 @@ int open(const char *pathname, int flags)
if (strcmp(pathname, "/dev/lbb") == 0)
{
hooks[BASEBOARD] = _open(HOOK_FILE_NAME, flags);
printf("Baseboard opened %d\n", hooks[BASEBOARD]);
return hooks[BASEBOARD];
}
if (strcmp(pathname, "/dev/i2c/0") == 0)
{
hooks[EEPROM] = _open(HOOK_FILE_NAME, flags);
printf("EEPROM opened %d\n", hooks[EEPROM]);
return hooks[EEPROM];
}
@ -178,7 +218,7 @@ int open(const char *pathname, int flags)
return -1;
hooks[SERIAL0] = _open(HOOK_FILE_NAME, flags);
printf("SERIAL0 Opened %d\n", hooks[SERIAL0]);
printf("Warning: SERIAL0 Opened %d\n", hooks[SERIAL0]);
return hooks[SERIAL0];
}
@ -188,7 +228,7 @@ int open(const char *pathname, int flags)
return -1;
hooks[SERIAL1] = _open(HOOK_FILE_NAME, flags);
printf("SERIAL1 opened %d\n", hooks[SERIAL1]);
printf("Warning: SERIAL1 opened %d\n", hooks[SERIAL1]);
return hooks[SERIAL1];
}
@ -206,18 +246,30 @@ int open64(const char *pathname, int flags)
return open(pathname, flags);
}
FILE *fopen(const char *restrict pathname, const char *restrict mode)
{
FILE *(*_fopen)(const char *restrict pathname, const char *restrict mode) = dlsym(RTLD_NEXT, "fopen");
// printf("fopen %s\n", pathname);
if (strcmp(pathname, "/root/lindbergrc") == 0)
{
return _fopen("lindbergrc", mode);
}
if ((strcmp(pathname, "/usr/lib/boot/logo.tga") == 0) || (strcmp(pathname, "/usr/lib/boot/logo.tga") == 0))
{
return _fopen("logo.tga", mode);
}
if (strcmp(pathname, "/usr/lib/boot/LucidaConsole_12.tga") == 0)
{
return _fopen("LucidaConsole_12.tga", mode);
}
if (strcmp(pathname, "/usr/lib/boot/LucidaConsole_12.abc") == 0)
{
return _fopen("LucidaConsole_12.abc", mode);
}
if (strcmp(pathname, "/proc/cpuinfo") == 0)
{
fileRead[CPUINFO] = 0;
@ -225,6 +277,26 @@ FILE *fopen(const char *restrict pathname, const char *restrict mode)
return fileHooks[CPUINFO];
}
if (strcmp(pathname, "/usr/lib/boot/logo_red.tga") == 0)
{
return _fopen("logo_red.tga", mode);
}
if (strcmp(pathname, "/usr/lib/boot/SEGA_KakuGothic-DB-Roman_12.tga") == 0)
{
return _fopen("SEGA_KakuGothic-DB-Roman_12.tga", mode);
}
if (strcmp(pathname, "/usr/lib/boot/SEGA_KakuGothic-DB-Roman_12.abc") == 0)
{
return _fopen("SEGA_KakuGothic-DB-Roman_12.abc", mode);
}
if (strcmp(pathname, "/proc/bus/pci/00/1f.0") == 0)
{
fileRead[PCI_CARD_1F0] = 0;
fileHooks[PCI_CARD_1F0] = _fopen(HOOK_FILE_NAME, mode);
return fileHooks[PCI_CARD_1F0];
}
return _fopen(pathname, mode);
}
@ -242,9 +314,42 @@ FILE *fopen64(const char *pathname, const char *mode)
return fileHooks[OSRELEASE];
}
if (strcmp(pathname, "/usr/lib/boot/logo_red.tga") == 0)
{
return _fopen64("logo_red.tga", mode);
}
if (strcmp(pathname, "/usr/lib/boot/logo.tga") == 0)
{
return _fopen64("logo.tga", mode);
}
if (strcmp(pathname, "/usr/lib/boot/SEGA_KakuGothic-DB-Roman_12.tga") == 0)
{
return _fopen64("SEGA_KakuGothic-DB-Roman_12.tga", mode);
}
if (strcmp(pathname, "/usr/lib/boot/SEGA_KakuGothic-DB-Roman_12.abc") == 0)
{
return _fopen64("SEGA_KakuGothic-DB-Roman_12.abc", mode);
}
return _fopen64(pathname, mode);
}
int fclose(FILE *stream)
{
int (*_fclose)(FILE *stream) = dlsym(RTLD_NEXT, "fclose");
for (int i = 0; i < 3; i++)
{
if (fileHooks[i] == stream)
{
int r = _fclose(stream);
fileHooks[i] = NULL;
return r;
}
}
return _fclose(stream);
}
int openat(int dirfd, const char *pathname, int flags)
{
int (*_openat)(int dirfd, const char *pathname, int flags) = dlsym(RTLD_NEXT, "openat");
@ -336,6 +441,18 @@ ssize_t read(int fd, void *buf, size_t count)
return _read(fd, buf, count);
}
size_t fread(void *buf, size_t size, size_t count, FILE *stream)
{
size_t (*_fread)(void *buf, size_t size, size_t count, FILE *stream) = dlsym(RTLD_NEXT, "fread");
if (stream == fileHooks[PCI_CARD_1F0])
{
memcpy(buf, pcidata, 68);
return 68;
}
return _fread(buf, size, count, stream);
}
ssize_t write(int fd, const void *buf, size_t count)
{
int (*_write)(int fd, const void *buf, size_t count) = dlsym(RTLD_NEXT, "write");
@ -428,15 +545,14 @@ int system(const char *command)
return 0;
if (strcmp(command, "uname -r | grep mvl") == 0)
{
EmulatorConfig *config = getConfig();
config->game = SEGABOOT_2_4;
return 0;
}
if (strstr(command, "hwclock") != NULL)
return 0;
if (strstr(command, "losetup") != NULL)
return 0;
return _system(command);
}
@ -465,7 +581,7 @@ float powf(float base, float exponent)
return (float)pow((double)base, (double)exponent);
}
/** This might be required for some games
/*
int sem_wait(sem_t *sem)
{
int (*original_sem_wait)(sem_t * sem) = dlsym(RTLD_NEXT, "sem_wait");
@ -476,6 +592,10 @@ int sem_wait(sem_t *sem)
/**
* Hook function used by Harley Davidson to change IPs to localhost
* Currently does nothing.
* @param sockfd
* @param addr
* @param addrlen
* @return
*/
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
{
@ -486,11 +606,60 @@ int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
// Change the IP to connect to to 127.0.0.1
// in_pointer->sin_addr.s_addr = inet_addr("127.0.0.1");
char *some_addr = inet_ntoa(in_pointer->sin_addr);
printf("Connecting to %s\n", some_addr);
if (getConfig()->showDebugMessages)
{
printf("Connecting to %s\n", some_addr);
}
return _connect(sockfd, addr, addrlen);
}
/**
* Function to calculate CRC32 checksum in memory.
*/
uint32_t get_crc32(const char *s, size_t n)
{
uint32_t crc = 0xFFFFFFFF;
for (size_t i = 0; i < n; i++)
{
char ch = s[i];
for (size_t j = 0; j < 8; j++)
{
uint32_t b = (ch ^ crc) & 1;
crc >>= 1;
if (b)
crc = crc ^ 0xEDB88320;
ch >>= 1;
}
}
return ~crc;
}
/**
* Callback function to get the offset and size of the execution program in memory of the ELF we hook to.
*/
static int callback(struct dl_phdr_info *info, size_t size, void *data)
{
if ((info->dlpi_phnum >= 3) && (info->dlpi_phdr[2].p_type == PT_LOAD) && (info->dlpi_phdr[2].p_flags == 5))
{
elf_crc = get_crc32((void *)(info->dlpi_addr + info->dlpi_phdr[2].p_vaddr + 10), 128);
}
return 1;
}
void getCPUID()
{
unsigned eax;
eax = 0;
__get_cpuid(0, &eax, &cpu_vendor.ebx, &cpu_vendor.ecx, &cpu_vendor.edx);
sprintf(cpu_vendor.cpuid, "%.4s%.4s%.4s", (const char *)&cpu_vendor.ebx, (const char *)&cpu_vendor.edx, (const char *)&cpu_vendor.ecx);
if (getConfig()->showDebugMessages)
{
printf("Detected CPU Vendor: %s\n", cpu_vendor.cpuid);
}
}
/**
* Stop the game changing the DISPLAY environment variable
*/
@ -506,6 +675,34 @@ int setenv(const char *name, const char *value, int overwrite)
return _setenv(name, value, overwrite);
}
/**
* Fake the TEA_DIR environment variable to games that require it to run
*/
char *getenv(const char *name)
{
char *(*_getenv)(const char *name) = dlsym(RTLD_NEXT, "getenv");
if ((strcmp(name, "TEA_DIR") == 0) && ((getConfig()->game == VIRTUA_TENNIS_3) || (getConfig()->game == VIRTUA_TENNIS_3_TEST) ||
((getConfig()->game == RAMBO)) || (getConfig()->game == TOO_SPICY)))
{
if (getcwd(envpath, 100) == NULL)
return "";
char *ptr = strrchr(envpath, '/');
if (ptr == NULL)
return "";
*ptr = '\0';
return envpath;
}
else if (strcmp(name, "TEA_DIR") == 0)
{
if (getcwd(envpath, 100) == NULL)
return "";
return envpath;
}
return _getenv(name);
}
/**
* Stop the game unsetting the DISPLAY environment variable
*/

View File

@ -0,0 +1,10 @@
uint32_t get_crc32(const char *s,size_t n);
void getCPUID();
typedef struct
{
unsigned ebx;
unsigned edx;
unsigned ecx;
char cpuid[20];
}cpuvendor;

118
src/lindbergh/input.c Normal file
View File

@ -0,0 +1,118 @@
#define _GNU_SOURCE
#include "jvs.h"
#include "config.h"
#include <GL/freeglut.h>
#include <GL/glx.h>
#include <X11/extensions/xf86vmode.h>
#include <X11/Xatom.h>
#include <dlfcn.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <pthread.h>
#include <math.h>
int initInput()
{
return 0;
}
int XNextEvent(Display *display, XEvent *event_return)
{
int (*_XNextEvent)(Display *display, XEvent *event_return) = dlsym(RTLD_NEXT, "XNextEvent");
int returnValue = _XNextEvent(display, event_return);
// Return now if we're not emulating JVS
if (!getConfig()->emulateJVS)
{
return returnValue;
}
switch (event_return->type)
{
case KeyRelease:
case KeyPress:
{
switch (event_return->xkey.keycode)
{
case 28:
setSwitch(SYSTEM, BUTTON_TEST, event_return->type == KeyPress);
break;
case 39:
setSwitch(PLAYER_1, BUTTON_SERVICE, event_return->type == KeyPress);
break;
case 14:
incrementCoin(PLAYER_1, event_return->type == KeyPress);
break;
case 15:
incrementCoin(PLAYER_2, event_return->type == KeyPress);
break;
case 111:
setSwitch(PLAYER_1, BUTTON_UP, event_return->type == KeyPress);
break;
case 116:
setSwitch(PLAYER_1, BUTTON_DOWN, event_return->type == KeyPress);
break;
case 113:
setSwitch(PLAYER_1, BUTTON_LEFT, event_return->type == KeyPress);
break;
case 114:
setSwitch(PLAYER_1, BUTTON_RIGHT, event_return->type == KeyPress);
break;
case 10:
setSwitch(PLAYER_1, BUTTON_START, event_return->type == KeyPress);
break;
case 24:
setSwitch(PLAYER_1, BUTTON_1, event_return->type == KeyPress);
break;
case 25:
setSwitch(PLAYER_1, BUTTON_2, event_return->type == KeyPress);
break;
case 26:
setSwitch(PLAYER_1, BUTTON_3, event_return->type == KeyPress);
break;
case 27:
setSwitch(PLAYER_1, BUTTON_4, event_return->type == KeyPress);
break;
default:
break;
}
}
break;
case MotionNotify:
{
setAnalogue(ANALOGUE_1, ((double)event_return->xmotion.x / (double)getConfig()->width) * pow(2, 10));
setAnalogue(ANALOGUE_2, ((double)event_return->xmotion.y / (double)getConfig()->height) * pow(2, 10));
}
break;
case ButtonPress:
case ButtonRelease:
{
switch (event_return->xbutton.button)
{
case 1: // Trigger
setSwitch(PLAYER_1, BUTTON_1, event_return->type == ButtonPress);
break;
case 3: // Reload
setSwitch(PLAYER_1, BUTTON_2, event_return->type == ButtonPress);
break;
case 9: // Gun Button
setSwitch(PLAYER_1, BUTTON_3, event_return->type == ButtonPress);
break;
default:
break;
}
}
break;
}
return returnValue;
}

1
src/lindbergh/input.h Normal file
View File

@ -0,0 +1 @@
int initInput();

View File

@ -3,6 +3,7 @@
#include <time.h>
#include <string.h>
#include <math.h>
#include <pthread.h> /* POSIX threads API to create and manage threads in the program */
/* The in and out packets used to read and write to and from*/
JVSPacket inputPacket, outputPacket;
@ -13,6 +14,8 @@ unsigned char outputBuffer[JVS_MAX_PACKET_SIZE], inputBuffer[JVS_MAX_PACKET_SIZE
/* Holds the status of the sense line */
int senseLine = 3;
pthread_mutex_t jvsMutex = PTHREAD_MUTEX_INITIALIZER;
JVSIO io = {0};
/**
@ -30,9 +33,9 @@ int initJVS()
io.capabilities.switches = 14;
io.capabilities.coins = 2;
io.capabilities.players = 2;
io.capabilities.analogueInBits = 8;
io.capabilities.analogueInBits = 10;
io.capabilities.rightAlignBits = 0;
io.capabilities.analogueInChannels = 20;
io.capabilities.analogueInChannels = 8;
io.capabilities.generalPurposeOutputs = 20;
io.capabilities.commandVersion = 19;
io.capabilities.jvsVersion = 48;
@ -160,7 +163,7 @@ void writeFeatures(JVSPacket *outputPacket, JVSCapabilities *capabilities)
*
* @returns The status of the entire operation
*/
JVSStatus processPacket()
JVSStatus processPacket(int *packetSize)
{
readPacket(&inputPacket);
@ -177,6 +180,8 @@ JVSStatus processPacket()
/* Set the entire packet success line */
outputPacket.data[outputPacket.length++] = STATUS_SUCCESS;
pthread_mutex_lock(&jvsMutex);
while (index < inputPacket.length - 1)
{
int size = 1;
@ -260,6 +265,7 @@ JVSStatus processPacket()
outputPacket.data[outputPacket.length] = REPORT_SUCCESS;
outputPacket.data[outputPacket.length + 1] = io.state.inputSwitch[0];
outputPacket.length += 2;
for (int i = 0; i < inputPacket.data[index + 1]; i++)
{
for (int j = 0; j < inputPacket.data[index + 2]; j++)
@ -272,7 +278,7 @@ JVSStatus processPacket()
case CMD_READ_COINS:
{
////printf("CMD_READ_COINS\n");
// printf("CMD_READ_COINS\n");
size = 2;
int numberCoinSlots = inputPacket.data[index + 1];
outputPacket.data[outputPacket.length++] = REPORT_SUCCESS;
@ -288,7 +294,7 @@ JVSStatus processPacket()
case CMD_READ_ANALOGS:
{
// printf("CMD_READ_ANALOGS\n");
// printf("CMD_READ_ANALOGS %d\n", inputPacket.data[index + 1]);
size = 2;
outputPacket.data[outputPacket.length++] = REPORT_SUCCESS;
@ -470,7 +476,9 @@ JVSStatus processPacket()
index += size;
}
writePacket(&outputPacket);
pthread_mutex_unlock(&jvsMutex);
writePacket(&outputPacket, packetSize);
return JVS_STATUS_SUCCESS;
}
@ -557,7 +565,7 @@ JVSStatus readPacket(JVSPacket *packet)
*
* @param packet The packet to send
*/
JVSStatus writePacket(JVSPacket *packet)
JVSStatus writePacket(JVSPacket *packet, int *packetSize)
{
/* Get pointer to raw data in packet */
unsigned char *packetPointer = (unsigned char *)packet;
@ -595,6 +603,9 @@ JVSStatus writePacket(JVSPacket *packet)
outputBuffer[outputIndex++] = checksum;
}
// Communicate the output size based on the now escaped bytes
*packetSize = outputIndex;
return JVS_STATUS_SUCCESS;
}
@ -623,7 +634,6 @@ int setSwitch(JVSPlayer player, JVSInput switchNumber, int value)
{
io.state.inputSwitch[player] &= ~switchNumber;
}
return 1;
}
@ -638,7 +648,10 @@ int incrementCoin(JVSPlayer player, int amount)
int setAnalogue(JVSInput channel, int value)
{
pthread_mutex_lock(&jvsMutex);
io.state.analogueChannel[channel] = value;
pthread_mutex_unlock(&jvsMutex);
return 1;
}

View File

@ -223,10 +223,10 @@ typedef enum
int initJVS();
JVSStatus processPacket();
JVSStatus processPacket(int* packetSize);
JVSStatus readPacket(JVSPacket *packet);
JVSStatus writePacket(JVSPacket *packet);
JVSStatus writePacket(JVSPacket *packet, int* packetSize);
/* The in and out packets used to read and write to and from*/
extern JVSPacket inputPacket, outputPacket;

179
src/lindbergh/lindbergh.c Normal file
View File

@ -0,0 +1,179 @@
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define LD_LIBRARY_PATH "LD_LIBRARY_PATH"
#define LD_PRELOAD "LD_PRELOAD"
#define PRELOAD_FILE_NAME "lindbergh.so"
// List of all lindbergh executables known, not including the test executables
char *games[] = {"Jennifer", "segaboot", "amiM.elf", "abc", "hod4M.elf", "lgj_final", "vt3", "id4.elf", "id5.elf", "lgjsp_app", "gsevo", "vf5", "apacheM.elf", "END"};
/**
* Tests if the game uses a seperate elf for test mode
* and updates the command line to this elf.
*
* @param name The command path to run
*/
void testModePath(char *name)
{
// Check if a different testmode elf is used
if (strcmp(name, "./hod4M.elf") == 0)
{
strcpy(name, "./hod4testM.elf");
return;
}
if (strcmp(name, "./Jennifer") == 0)
{
strcpy(name, "../JenTest/JenTest");
return;
}
if (strcmp(name, "./apacheM.elf") == 0)
{
strcpy(name, "./apacheTestM.elf");
return;
}
// Otherwise add the standard -t to the end
strcat(name, " -t");
}
/**
* Makes sure the environment variables are set correctly
* to run the game.
*/
void setEnvironmentVariables()
{
// Ensure the library path is set correctly
char libraryPath[128] = {0};
const char *currentLibraryPath = getenv(LD_LIBRARY_PATH);
if (currentLibraryPath != NULL)
{
strcat(libraryPath, currentLibraryPath);
strcat(libraryPath, ":");
}
strcat(libraryPath, ".:lib");
setenv(LD_LIBRARY_PATH, libraryPath, 1);
// Ensure the preload path is set correctly
setenv(LD_PRELOAD, PRELOAD_FILE_NAME, 1);
}
/**
* Prints the usage for the loader
*/
void printUsage(char *argv[])
{
printf("%s [GAME_PATH] [OPTIONS]\n", argv[0]);
printf("Options:\n");
printf(" --test | -t Runs the test mode\n");
printf(" --gdb Runs with GDB\n");
printf(" --help Displays this usage text\n");
}
/**
* Small utility to automatically detect the game and run it without
* having to type a long string in.
*/
int main(int argc, char *argv[])
{
// Ensure environment variables are set correctly
setEnvironmentVariables();
// Look for the games
struct dirent *ent;
DIR *dir = opendir(".");
if (dir == NULL)
{
printf("Error: Could not list files in current directory.\n");
return EXIT_FAILURE;
}
char *game = NULL;
int lindberghSharedObjectFound = 0;
while ((ent = readdir(dir)) != NULL)
{
if (ent->d_type != DT_REG)
continue;
if (strcmp(ent->d_name, PRELOAD_FILE_NAME) == 0)
{
lindberghSharedObjectFound = 1;
continue;
}
int index = 0;
while (game == NULL)
{
if (strcmp(games[index], "END") == 0)
break;
if (strcmp(ent->d_name, games[index]) == 0)
{
game = games[index];
break;
}
index++;
}
if (game != NULL && lindberghSharedObjectFound)
break;
}
closedir(dir);
if (argc > 1 && strcmp(argv[1], "--help") == 0)
{
printUsage(argv);
return EXIT_SUCCESS;
}
if (!lindberghSharedObjectFound)
{
printf("Error: The preload object lindbergh.so was not found in this directory.\n");
return EXIT_FAILURE;
}
if (game == NULL)
{
printf("Error: No lindbergh game found in this directory.\n");
return EXIT_FAILURE;
}
// Build up the command to start the game
char command[128];
strcpy(command, "./");
strcat(command, game);
for (int i = 1; i < argc; i++)
{
if (strcmp(argv[i], "-t") == 0 || strcmp(argv[i], "--test") == 0)
{
testModePath(command);
}
if (strcmp(argv[i], "--gdb") == 0)
{
char temp[128];
strcpy(temp, "gdb ");
strcat(temp, command);
strcpy(command, temp);
}
}
printf("$ %s\n", command);
return system(command);
}

View File

@ -1,10 +1,15 @@
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include <sys/mman.h>
#include <unistd.h>
#include "patch.h"
#include "config.h"
#include "hook.h"
#include "securityboard.h"
extern cpuvendor cpu_vendor;
static void setVariable(uint32_t address, uint32_t value)
{
@ -14,14 +19,49 @@ static void setVariable(uint32_t address, uint32_t value)
void *toModify = (void *)(address - (address % pagesize));
int prot = mprotect(toModify, pagesize, PROT_WRITE);
int prot = mprotect(toModify, pagesize, PROT_EXEC | PROT_WRITE);
if (prot != 0)
{
printf("Error: Cannot unprotect memory region to change variable (%d)\n", prot);
return;
}
*variable = value;
}
static void patchMemory(uint32_t address, char *value)
{
size_t size = strlen((void *)value);
// printf("Size=%d\n", size);
if (size % 2 != 0)
{
printf("Patch value should be even.\n");
exit(1);
}
char buf[size / 2];
char tmpchr[3];
char *p = value;
for (int i = 0; i < size; i++)
{
memcpy(tmpchr, p, 2);
tmpchr[2] = '\0';
buf[i] = (int)strtol(tmpchr, NULL, 16);
p += 2;
}
int pagesize = sysconf(_SC_PAGE_SIZE);
void *toModify = (void *)(address - (address % pagesize));
int prot = mprotect(toModify, pagesize, PROT_EXEC | PROT_WRITE);
if (prot != 0)
{
printf("Error: Cannot unprotect memory region to change variable (%d)\n", prot);
return;
}
*variable = value;
memcpy((uint32_t *)address, buf, size / 2);
}
static void detourFunction(uint32_t address, void *function)
@ -44,11 +84,21 @@ static void detourFunction(uint32_t address, void *function)
cave[4] = (jumpAddress >> (8 * 3)) & 0xFF;
cave[3] = (jumpAddress >> (8 * 2)) & 0xFF;
cave[2] = (jumpAddress >> (8 * 1)) & 0xFF;
cave[1] = (jumpAddress)&0xFF;
cave[1] = (jumpAddress) & 0xFF;
memcpy((void *)address, cave, 5);
}
int stubRetZero()
{
return 0;
}
int stubRetOne()
{
return 1;
}
int amDongleInit()
{
return 0;
@ -64,81 +114,638 @@ int amDongleUpdate()
return 0;
}
int amLibInit()
{
uint32_t *amLibContext = (uint32_t *)0x08dfa2c0; // 0x0809cb00;
*amLibContext = 1;
uint32_t *amLibInitializad = (uint32_t *)0x08dfa2c4; // 0x0809cb04;
uint16_t *amLibPort1 = (uint16_t *)(0x08dfa2c4 + 4); //(0x0809cb04 + 4);
uint16_t *amLibPort2 = (uint16_t *)(0x08dfa2c4 + 4); //(0x0809cb04 + 6);
uint32_t *bcLibInitialized = (uint32_t *)(0x08dfa2c4 + 8); // 0x0809cb0c;
*amLibInitializad = 1;
*amLibPort1 = 0xd000;
*amLibPort2 = 0x0004;
*bcLibInitialized = 0;
int res = ((int (*)(void))0x084dedc4)(); // 0x08065d80)();
if (res == 1)
*bcLibInitialized = 1;
return 0;
}
int amDipswInit()
{
uint32_t *amDipswContext = (uint32_t *)0x08df9cec; // 0x0809c12c;
uint32_t *amDipswContext1 = (uint32_t *)(0x08df9cec + 4); //(0x0809c12c + 4);
uint32_t *amDipswContext2 = (uint32_t *)(0x08df9cec + 8); //(0x0809c12c + 8);
uint32_t *amDipswContext3 = (uint32_t *)(0x08df9cec + 12); //(0x0809c12c + 12);
// typedef void *(*___constant_c_and_count_memset)(uint32_t *, int, size_t);
//___constant_c_and_count_memset func = (___constant_c_and_count_memset)//0x0805c3d5;
// func(amDipswContext, 0, 4);
*amDipswContext = 1;
*amDipswContext1 = 1;
*amDipswContext2 = 1;
*amDipswContext3 = 1;
return 0;
}
void print_binary(unsigned int number)
{
if (number >> 1)
{
print_binary(number >> 1);
}
putc((number & 1) ? '1' : '0', stdout);
}
int amDipswGetData(uint8_t *dip)
{
// printf("amDipswGetData Called!!!!!\n");
uint8_t result;
uint32_t data;
securityBoardIn(0x38, &data);
result = (~data & 4) != 0; // Test Button
if ((~data & 8) != 0)
result |= 2; // Service Button
if ((~data & 0x10) != 0)
result |= 4; // ??
if ((char)data >= 0)
result |= 8; // ??
if ((~data & 0x100) != 0)
result |= 0x10; // Rotation
if ((~data & 0x200) != 0)
result |= 0x20; // Resolution Dip 4
if ((~data & 0x400) != 0)
result |= 0x40; // Resolution Dip 5
if ((~data & 0x800) != 0)
result |= 0x80; // Resolution Dip 6
*dip = result;
return 0;
}
void _putConsole(const char *format, ...)
{
va_list args;
va_start(args, format);
while (*format)
{
if (*format == '%')
{
format++;
if ((*format == 'd') || (*format == 'n'))
{
printf("%d", va_arg(args, int));
}
else if (*format == 's')
{
printf("%s", va_arg(args, char *));
}
else if (*format == 'u')
{
printf("%u", va_arg(args, unsigned int));
}
else if (*format == '0')
{
format++;
if (*format == '2')
{
format++;
printf("%02X", va_arg(args, int));
}
else if (*format == '4')
{
format++;
printf("%04X", va_arg(args, unsigned int));
}
}
else
{
printf("\nFormat: %c.\n", *format);
}
}
else
{
putchar(*format);
}
format++;
}
va_end(args);
printf("\n");
}
int initPatch()
{
Game game = getConfig()->game;
EmulatorConfig *config = getConfig();
switch (game)
switch (config->game)
{
case RTUNED:
case R_TUNED:
{
// Security
detourFunction(0x08366846, amDongleInit);
detourFunction(0x08365301, amDongleIsAvailable);
detourFunction(0x08365cf7, amDongleUpdate);
}
break;
case SRTV:
case SEGA_RACE_TV:
{
// Security
detourFunction(0x084d5b40, amDongleInit);
detourFunction(0x084d45f9, amDongleIsAvailable);
detourFunction(0x084d4fef, amDongleUpdate);
// Fixes
detourFunction(0x084d44fc, stubRetZero); // Stub amDipswSetLed
detourFunction(0x084d4485, amDipswGetData);
}
break;
case ABC:
case AFTER_BURNER_CLIMAX_REVA:
{
if (config->showDebugMessages == 1)
{
// Debug Messages
setVariable(0x0a0a37e4, 2); // amBackupDebugLevel
setVariable(0x0a0a3800, 2); // amCreditDebugLevel
setVariable(0x0a0a3a58, 2); // amDipswDebugLevel
setVariable(0x0a0a3a5c, 2); // amDongleDebugLevel
setVariable(0x0a0a3a60, 2); // amEepromDebugLevel
setVariable(0x0a0a3a64, 2); // amHwmonitorDebugLevel
setVariable(0x0a0a3a68, 2); // amJvsDebugLevel
setVariable(0x0a0a3a6c, 2); // amLibDebugLevel
setVariable(0x0a0a3a70, 2); // amMiscDebugLevel
setVariable(0x0a0a3a74, 2); // amOsinfoDebugLevel
setVariable(0x0a0a3a78, 2); // amSysDataDebugLevel
setVariable(0x0a0a3a80, 2); // bcLibDebugLevel
}
// Security
detourFunction(0x081e4980, amDongleInit);
detourFunction(0x081e4cce, amDongleIsAvailable);
detourFunction(0x081e4bfa, amDongleUpdate);
// Fixes
detourFunction(0x081e48b6, amDipswGetData);
detourFunction(0x081e492e, stubRetZero); // Stub amDipswSetLed
// Does not work
// setVariable(0x08061c31, 0x0000000c); // Force HD resolution
}
break;
case AFTER_BURNER_CLIMAX_REVB:
{
if (config->showDebugMessages == 1)
{
// Debug Messages
setVariable(0x0a0a0d24, 2); // amBackupDebugLevel
setVariable(0x0a0a0d40, 2); // amCreditDebugLevel
setVariable(0x0a0a0f98, 2); // amDipswDebugLevel
setVariable(0x0a0a0f9c, 2); // amDongleDebugLevel
setVariable(0x0a0a0fa0, 2); // amEepromDebugLevel
setVariable(0x0a0a0fa4, 2); // amHwmonitorDebugLevel
setVariable(0x0a0a0fa8, 2); // amJvsDebugLevel
setVariable(0x0a0a0fac, 2); // amLibDebugLevel
setVariable(0x0a0a0fb0, 2); // amMiscDebugLevel
setVariable(0x0a0a0fb4, 2); // amOsinfoDebugLevel
setVariable(0x0a0a0fb8, 2); // amSysDataDebugLevel
setVariable(0x0a0a0fc0, 2); // bcLibDebugLevel
}
// Security
detourFunction(0x081e3424, amDongleInit);
detourFunction(0x081e3772, amDongleIsAvailable);
detourFunction(0x081e369e, amDongleUpdate);
// Fixes
detourFunction(0x081e335a, amDipswGetData);
detourFunction(0x081e33d2, stubRetZero); // Stub amDipswSetLed
}
break;
case OUTRUN:
case OUTRUN_2_SP_SDX_REVA:
{
setVariable(0x0893a24c, 2); // amBackupDebugLevel
setVariable(0x0893a260, 2); // amCreditDebugLevel
setVariable(0x0893a4b8, 2); // amDipswDebugLevel
setVariable(0x0893a4bc, 2); // amDongleDebugLevel
setVariable(0x0893a4c0, 2); // amEepromDebugLevel
setVariable(0x0893a4c4, 2); // amHwmonitorDebugLevel
setVariable(0x0893a4c8, 2); // amJvsDebugLevel
setVariable(0x0893a4cc, 2); // amLibDebugLevel
setVariable(0x0893a4d0, 2); // amMiscDebugLevel
setVariable(0x0893a4d4, 2); // amOsinfoDebugLevel
setVariable(0x0893a4d8, 2); // amSysDataDebugLevel
setVariable(0x0893a4e0, 2); // bcLibDebugLevel
if (config->showDebugMessages == 1)
{
// Debug Messages
setVariable(0x0893a24c, 2); // amBackupDebugLevel
setVariable(0x0893a260, 2); // amCreditDebugLevel
setVariable(0x0893a4b8, 2); // amDipswDebugLevel
setVariable(0x0893a4bc, 2); // amDongleDebugLevel
setVariable(0x0893a4c0, 2); // amEepromDebugLevel
setVariable(0x0893a4c4, 2); // amHwmonitorDebugLevel
setVariable(0x0893a4c8, 2); // amJvsDebugLevel
setVariable(0x0893a4cc, 2); // amLibDebugLevel
setVariable(0x0893a4d0, 2); // amMiscDebugLevel
setVariable(0x0893a4d4, 2); // amOsinfoDebugLevel
setVariable(0x0893a4d8, 2); // amSysDataDebugLevel
setVariable(0x0893a4e0, 2); // bcLibDebugLevel
}
// Security
detourFunction(0x08190e80, amDongleInit);
detourFunction(0x08191201, amDongleIsAvailable);
detourFunction(0x08191125, amDongleUpdate);
// Fixes
detourFunction(0x08190db6, amDipswGetData);
detourFunction(0x08190e2e, stubRetZero); // Stub amDipswSetLed
}
break;
case THE_HOUSE_OF_THE_DEAD_4:
{
setVariable(0x0a737c60, 2); // amBackupDebugLevel
setVariable(0x0a737c64, 2); // amChunkDataDebugLevel
setVariable(0x0a737c80, 2); // amCreditDebugLevel
setVariable(0x0a737ed8, 2); // amDipswDebugLevel
setVariable(0x0a737edc, 2); // amDiskDebugLevel
setVariable(0x0a737ee0, 2); // amDongleDebugLevel
setVariable(0x0a737ee4, 2); // amEepromDebugLevel
setVariable(0x0a737ee8, 2); // amHmDebugLevel
setVariable(0x0a737ef0, 2); // amJvsDebugLevel
setVariable(0x0a737f14, 2); // amLibDebugLevel
setVariable(0x0a737f18, 2); // amMiscDebugLevel
setVariable(0x0a737f1c, 2); // amSysDataDebugLevel
setVariable(0x0a737f20, 2); // bcLibDebugLevel
setVariable(0x0a737f24, 0x0FFFFFFF); // s_logMask
if (config->showDebugMessages == 1)
{
// Debug Messages
setVariable(0x0a737c60, 2); // amBackupDebugLevel
setVariable(0x0a737c64, 2); // amChunkDataDebugLevel
setVariable(0x0a737c80, 2); // amCreditDebugLevel
setVariable(0x0a737ed8, 2); // amDipswDebugLevel
setVariable(0x0a737edc, 2); // amDiskDebugLevel
setVariable(0x0a737ee0, 2); // amDongleDebugLevel
setVariable(0x0a737ee4, 2); // amEepromDebugLevel
setVariable(0x0a737ee8, 2); // amHmDebugLevel
setVariable(0x0a737ef0, 2); // amJvsDebugLevel
setVariable(0x0a737f14, 2); // amLibDebugLevel
setVariable(0x0a737f18, 2); // amMiscDebugLevel
setVariable(0x0a737f1c, 2); // amSysDataDebugLevel
setVariable(0x0a737f20, 2); // bcLibDebugLevel
setVariable(0x0a737f24, 0x0FFFFFFF); // s_logMask
}
// Security
detourFunction(0x08320178, amDongleInit);
detourFunction(0x08320459, amDongleIsAvailable);
detourFunction(0x083203c0, amDongleUpdate);
// Fixes
detourFunction(0x0831ddd7, amDipswGetData);
detourFunction(0x0831de4f, stubRetZero); // Stub amDipswSetLed
// CPU patch to support AMD processors
if (strcmp("AuthenticAMD", cpu_vendor.cpuid) == 0)
{
setVariable(0x0837d6aa, cpu_vendor.ebx);
setVariable(0x0837d6ba, cpu_vendor.edx);
setVariable(0x0837d6c5, cpu_vendor.ecx);
}
}
break;
case THE_HOUSE_OF_THE_DEAD_4_STRIPPED:
{
// Security
detourFunction(0x0831ad04, amDongleInit);
detourFunction(0x0831b017, amDongleIsAvailable);
detourFunction(0x0831af7e, amDongleUpdate);
// Fixes
detourFunction(0x0831875f, amDipswGetData);
detourFunction(0x083187d7, stubRetZero); // Stub amDipswSetLed
// CPU patch to support AMD processors
if (strcmp("AuthenticAMD", cpu_vendor.cpuid) == 0)
{
setVariable(0x0837963a, cpu_vendor.ebx);
setVariable(0x0837964a, cpu_vendor.edx);
setVariable(0x08379655, cpu_vendor.ecx);
}
}
break;
case THE_HOUSE_OF_THE_DEAD_4_TEST:
{
detourFunction(0x080677a0, amDongleInit);
detourFunction(0x08067a81, amDongleIsAvailable);
detourFunction(0x080679e8, amDongleUpdate);
// Fixes
detourFunction(0x08067653, amDipswGetData);
detourFunction(0x080676cb, stubRetZero); // Stub amDipswSetLed
// CPU patch to support AMD processors
if (strcmp("AuthenticAMD", cpu_vendor.cpuid) == 0)
{
setVariable(0x0807217a, cpu_vendor.ebx);
setVariable(0x0807218a, cpu_vendor.edx);
setVariable(0x08072195, cpu_vendor.ecx);
}
}
break;
case THE_HOUSE_OF_THE_DEAD_4_SPECIAL:
{
detourFunction(0x08363438, amDongleInit);
detourFunction(0x0836374b, amDongleIsAvailable);
detourFunction(0x083636b2, amDongleUpdate);
patchMemory(0x081f9491, "9090");
patchMemory(0x081f9499, "01");
// Fixes
detourFunction(0x08360e93, amDipswGetData);
detourFunction(0x08360f0b, stubRetZero); // Stub amDipswSetLed
// CPU patch to support AMD processors
if (strcmp("AuthenticAMD", cpu_vendor.cpuid) == 0)
{
setVariable(0x083cef0a, cpu_vendor.ebx);
setVariable(0x083cef1a, cpu_vendor.edx);
setVariable(0x083cef25, cpu_vendor.ecx);
}
}
break;
case THE_HOUSE_OF_THE_DEAD_4_SPECIAL_TEST:
{
detourFunction(0x0806e914, amDongleInit);
detourFunction(0x0806ec27, amDongleIsAvailable);
detourFunction(0x0806eb8e, amDongleUpdate);
// Fixes
detourFunction(0x0806e7c7, amDipswGetData);
detourFunction(0x0806e83f, stubRetZero); // Stub amDipswSetLed
// CPU patch to support AMD processors
if (strcmp("AuthenticAMD", cpu_vendor.cpuid) == 0)
{
setVariable(0x0807a3ba, cpu_vendor.ebx);
setVariable(0x0807a3ca, cpu_vendor.edx);
setVariable(0x0807a3d5, cpu_vendor.ecx);
}
}
break;
case THE_HOUSE_OF_THE_DEAD_EX:
{
detourFunction(0x084ba886, amDongleInit);
detourFunction(0x084b9341, amDongleIsAvailable);
detourFunction(0x084b9d37, amDongleUpdate);
// Fixes
detourFunction(0x084b6a69, amDipswGetData);
detourFunction(0x084b6adf, stubRetZero); // Stub amDipswSetLed
// CPU patch to support AMD processors
if (strcmp("AuthenticAMD", cpu_vendor.cpuid) == 0)
{
setVariable(0x0849E2AD, cpu_vendor.ebx);
setVariable(0x0849E2B7, cpu_vendor.edx);
setVariable(0x0849E2C1, cpu_vendor.ecx);
}
}
break;
case THE_HOUSE_OF_THE_DEAD_EX_TEST:
{
detourFunction(0x08078996, amDongleInit);
detourFunction(0x08077451, amDongleIsAvailable);
detourFunction(0x08077e47, amDongleUpdate);
// Fixes
detourFunction(0x080772dd, amDipswGetData);
detourFunction(0x08077353, stubRetZero); // Stub amDipswSetLed
// CPU patch to support AMD processors
if (strcmp("AuthenticAMD", cpu_vendor.cpuid) == 0)
{
setVariable(0x080847BD, cpu_vendor.ebx);
setVariable(0x080847C7, cpu_vendor.edx);
setVariable(0x080847D1, cpu_vendor.ecx);
}
}
break;
case VIRTUA_FIGHTER_5_REVC:
{
// Security
detourFunction(0x085c6010, amDongleInit);
detourFunction(0x085c63cc, amDongleIsAvailable);
detourFunction(0x085c62f0, amDongleUpdate);
// Fixes and patches to bypss network check
detourFunction(0x085c5f46, amDipswGetData);
detourFunction(0x085c5fbe, stubRetZero); // Stub amDipswSetLed
detourFunction(0x080b3426, stubRetZero); // Stub returns 0
detourFunction(0x080cb6d4, stubRetZero); // Stub returns 0
detourFunction(0x0840889e, stubRetZero); // Stub returns 0
detourFunction(0x0840ab90, stubRetZero); // Stub returns 0
patchMemory(0x080e17af, "b800000000"); // Patch IDK what
}
break;
case LETS_GO_JUNGLE:
{
if (config->showDebugMessages == 1)
{
setVariable(0x08c083a4, 2); // amBackupDebugLevel
setVariable(0x08c083c0, 2); // amCreditDebugLevel
setVariable(0x08c08618, 2); // amDipswDebugLevel
setVariable(0x08c0861c, 2); // amDongleDebugLevel
setVariable(0x08c08620, 2); // amEepromDebugLevel
setVariable(0x08c08624, 2); // amHwmonitorDebugLevel
setVariable(0x08c08628, 2); // amJvsDebugLevel
setVariable(0x08c0862c, 2); // amLibDebugLevel
setVariable(0x08c08630, 2); // amMiscDebugLevel
setVariable(0x08c08638, 2); // amSysDataDebugLevel
setVariable(0x08c08640, 2); // bcLibDebugLevel
setVariable(0x08c08634, 2); // amOsinfoDebugLevel
setVariable(0x08c08644, 0x0FFFFFFF); // s_logMask
detourFunction(0x08074a8c, _putConsole); // Debug Messages
}
// Security
detourFunction(0x084e50d8, amDongleInit);
detourFunction(0x084e5459, amDongleIsAvailable);
detourFunction(0x084e537d, amDongleUpdate);
patchMemory(0x0807b76a, "9090"); // Patch initializeArcadeBackup
// Fixes
detourFunction(0x084e500e, amDipswGetData);
detourFunction(0x084e5086, stubRetZero); // Stub amDipswSetLed
patchMemory(0x0840d858, "9090"); // No more Full Screen from the Game
// Set Resolution
// setVariable(0x082E006b, 0x00000780); // Set ResX
// setVariable(0x082E0078, 0x00000438); // Set ResY
// From Teknoparrot AMDFIX
// setVariable(0x083ef701, 0x00036ee9); // AMDFIX
// setVariable(0x084032e0, 0x8b90c933); // fix shader compilation with AMD GPUs
// setVariable(0x08523950, 0x000000c3); // Remove ADXM_SetupFramework (Not necessary)
}
break;
case LETS_GO_JUNGLE_SPECIAL:
{
if (config->showDebugMessages == 1)
{
setVariable(0x08c453e4, 2); // amBackupDebugLevel
setVariable(0x08c45400, 2); // amCreditDebugLevel
setVariable(0x08c45658, 2); // amDipswDebugLevel
setVariable(0x08c4565c, 2); // amDongleDebugLevel
setVariable(0x08c45660, 2); // amEepromDebugLevel
setVariable(0x08c45664, 2); // amHwmonitorDebugLevel
setVariable(0x08c45668, 2); // amJvsDebugLevel
setVariable(0x08c4566c, 2); // amLibDebugLevel
setVariable(0x08c45670, 2); // amMiscDebugLevel
setVariable(0x08c45678, 2); // amSysDataDebugLevel
setVariable(0x08c45680, 2); // bcLibDebugLevel
setVariable(0x08c45674, 2); // amOsinfoDebugLevel
setVariable(0x08c45684, 0x0FFFFFFF); // s_logMask
detourFunction(0x08075012, _putConsole);
}
// Security
detourFunction(0x08510320, amDongleInit);
detourFunction(0x085106dc, amDongleIsAvailable);
detourFunction(0x08510600, amDongleUpdate);
patchMemory(0x0807e609, "909090909090");
// Fixes
detourFunction(0x08510256, amDipswGetData);
detourFunction(0x085102ce, stubRetZero); // Stub amDipswSetLed
patchMemory(0x08438954, "9090"); // No more Full Screen from the Game
// Set Resolution
// setVariable(0x08303C4B, 0x00000780); // Set ResX
// setVariable(0x08303C58, 0x00000438); // Set ResY
}
break;
case INITIALD_4:
{
if (config->showDebugMessages == 1)
{
setVariable(0x08d71750, 2); // amBackupDebugLevel
setVariable(0x08d71760, 2); // amCreditDebugLevel
setVariable(0x08d719b8, 2); // amDipswDebugLevel
setVariable(0x08d719bc, 2); // amDongleDebugLevel
setVariable(0x08d719c0, 2); // amEepromDebugLevel
setVariable(0x08d719c4, 2); // amHwmonitorDebugLevel
setVariable(0x08d719c8, 2); // amJvsDebugLevel
setVariable(0x08d719cc, 2); // amLibDebugLevel
setVariable(0x08d719d0, 2); // amMiscDebugLevel
setVariable(0x08d719d8, 2); // amSysDataDebugLevel
setVariable(0x08d719e0, 2); // bcLibDebugLevel
setVariable(0x08d719d4, 2); // amOsinfoDebugLevel
setVariable(0x08d719e4, 0x0FFFFFFF); // s_logMask
detourFunction(0x0808f9a8, _putConsole); // Crashes the game sometimes.
}
// Security
detourFunction(0x086e2336, amDongleInit);
detourFunction(0x086e0d81, amDongleIsAvailable);
detourFunction(0x086e17e5, amDongleUpdate);
// Fixes
detourFunction(0x086e0c0d, amDipswGetData);
detourFunction(0x086e0c84, stubRetZero); // amDipswSetLED
detourFunction(0x0821e6dc, stubRetOne); // isEthLinkUp
patchMemory(0x082cb412, "c0270900"); // tickInitStoreNetwork
patchMemory(0x082cb6d9, "e950010000"); // tickWaitDHCP
// Set Resolution
// patchMemory(0x0835664d, "e9f000"); // Force resolution set
// setVariable(0x08356743, 0x00000780); // Set ResX
// setVariable(0x08356748, 0x00000438); // Set ResY
// FrameBuffer Resolution (No effect that I know)
/*
setVariable(0x08248037, 0x00000780); // Set ResX
setVariable(0x0824802f, 0x00000438); // Set ResY
setVariable(0x082480f7, 0x00000780); // Set ResX
setVariable(0x082480ef, 0x00000438); // Set ResY
setVariable(0x082481b7, 0x00000780); // Set ResX
setVariable(0x082481af, 0x00000438); // Set ResY
setVariable(0x08248216, 0x00000780); // Set ResX
setVariable(0x0824820e, 0x00000438); // Set ResY
setVariable(0x082489a7, 0x00000780); // Set ResX
setVariable(0x0824899f, 0x00000438); // Set ResY
setVariable(0x08248a32, 0x00000780); // Set ResX
setVariable(0x08248a2a, 0x00000438); // Set ResY
*/
// IDK if the following work (taken from TP)
// setVariable(0x08548ef3, 0x8990c031); // Shader Compiler
// setVariable(0x08799d8c, 0x082c9f52); // childTerminationHanlder
}
break;
case INITIALD_4_REVE:
{
if (config->showDebugMessages == 1)
{
// Debug
detourFunction(0x08090478, _putConsole); // Crashes the game sometimes.
}
// Security
detourFunction(0x087106e6, amDongleInit);
detourFunction(0x0870f131, amDongleIsAvailable);
detourFunction(0x0870fb95, amDongleUpdate);
// Fixes
detourFunction(0x0870efbd, amDipswGetData);
detourFunction(0x0870f034, stubRetZero); // amDipswSetLed
detourFunction(0x08230fde, stubRetOne); // isEthLinkUp
patchMemory(0x082df87d, "e954010000"); // tickWaitDHCP
patchMemory(0x082e0ec9, "eb60"); // tickInitAddress
// setVariable(0x08580979, 0x000126e9); // Avoid Full Screen set from Game
// Set Resolution
// setVariable(0x0837b12d, 0x0000f0e9); // Force set resolution
// setVariable(0x0837b223, 0x00000550); // Set ResX
// setVariable(0x0837b228, 0x00000300); // Set ResY
// setVariable(0x085700d3, 0x8990c031); // Fix something with the Shaders??
}
break;
case SEGABOOT_2_4_SYM:
{
if (config->showDebugMessages == 1)
{
setVariable(0x0808da48, 2); // amAdtecDebugLevel
setVariable(0x0808cf8c, 2); // amAtaDebugLevel
setVariable(0x0808cf90, 2); // amBackupDebugLevel
setVariable(0x0808cf94, 2); // amChunkDataDebugLevel
setVariable(0x0808cfa0, 2); // amCreditDebugLevel
setVariable(0x0808d1f8, 2); // amDipswDebugLevel
setVariable(0x0808d1fc, 2); // amDiskDebugLevel
setVariable(0x0808d200, 2); // amDongleDebugLevel
setVariable(0x0808d204, 2); // amEepromDebugLevel
setVariable(0x0808d208, 2); // amHmDebugLevel
setVariable(0x0808d210, 2); // amJvsDebugLevel
setVariable(0x0808d214, 2); // amLibDebugLevel
setVariable(0x0808d218, 2); // amMiscDebugLevel
setVariable(0x0808d21c, 2); // amSysDataDebugLevel
setVariable(0x0808d220, 2); // bcLibDebugLevel
setVariable(0x0808cf58, 2); // g_DebugLevel
setVariable(0x0808d224, 0x0FFFFFFF); // logmask
}
detourFunction(0x0805e8b0, amDongleInit);
detourFunction(0x0805ebc3, amDongleIsAvailable);
detourFunction(0x0805eb2a, amDongleUpdate);
detourFunction(0x0805c30b, amDipswGetData);
}
break;
case VIRTUA_TENNIS_3:
{
// Security
detourFunction(0x0831c724, amDongleInit);
detourFunction(0x0831ca37, amDongleIsAvailable);
detourFunction(0x0831c99e, amDongleUpdate);
// Fixes
detourFunction(0x0831c5d7, amDipswGetData);
detourFunction(0x0831c64f, stubRetZero);
setVariable(0x0827ae1b, 0x34891beb); // Disable Fullscreen set from the game
}
break;
case VIRTUA_TENNIS_3_TEST:
{
if (config->showDebugMessages == 1)
{
// Debug
detourFunction(0x08054d14, _putConsole); // Crashes the game sometimes.
}
// Security
detourFunction(0x0815f610, amDongleInit);
detourFunction(0x0815f923, amDongleIsAvailable);
detourFunction(0x0815f88a, amDongleUpdate);
// Fixes
detourFunction(0x0815d06b, amDipswGetData);
detourFunction(0x0815d0e3, stubRetZero);
}
break;
case RAMBO:
{
// Security
detourFunction(0x082c4746, amDongleInit);
detourFunction(0x082c3201, amDongleIsAvailable);
detourFunction(0x082c3bf7, amDongleUpdate);
// Fixes
detourFunction(0x082c308d, amDipswGetData);
detourFunction(0x082c3103, stubRetZero);
}
break;
case TOO_SPICY:
{
// Security
detourFunction(0x0831cf02, amDongleInit);
detourFunction(0x0831b94d, amDongleIsAvailable);
detourFunction(0x0831c3b1, amDongleUpdate);
// Fixes
detourFunction(0x0831907d, amDipswGetData);
detourFunction(0x083190f4, stubRetZero);
// CPU patch to support AMD processors
if (strcmp("AuthenticAMD", cpu_vendor.cpuid) == 0)
{
setVariable(0x08399ADA, cpu_vendor.ebx);
setVariable(0x08399AEA, cpu_vendor.edx);
setVariable(0x08399AF5, cpu_vendor.ecx);
}
}
break;
default:
// Don't do any patches for random games
break;
}
return 0;
}

28
src/lindbergh/pcidata.h Normal file
View File

@ -0,0 +1,28 @@
const char pcidata[256] = {
0x86, 0x80, 0x18, 0x29, 0x03, 0x01, 0x00, 0x00, 0x02, 0x00, 0x01,
0x06, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xf4, 0x1a, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x06,
0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8a, 0x8a, 0x8b,
0x8b, 0x00, 0x00, 0x00, 0x00, 0x8a, 0x8a, 0x8b, 0x8b, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc0,
0xd1, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00
};
const int pcidata_length = 256;

View File

@ -106,26 +106,26 @@ int securityBoardIn(uint16_t port, uint32_t *data)
case SECURITY_BOARD_FRONT_PANEL:
{
uint32_t result = 0xFFFFFFFF;
if (securityBoard.serviceSwitch)
result &= ~0x08;
if (securityBoard.testSwitch)
result &= ~0x04;
if (securityBoard.dipSwitch[6])
if (securityBoard.dipSwitch[6]) // bit12
result &= ~0x800; // DIP 6
if (securityBoard.dipSwitch[5])
if (securityBoard.dipSwitch[5]) // bit11
result &= ~0x400; // DIP 5
if (securityBoard.dipSwitch[4])
if (securityBoard.dipSwitch[4]) // bit10
result &= ~0x200; // DIP 4
if (securityBoard.dipSwitch[3])
if (securityBoard.dipSwitch[3]) // bit9
result &= ~0x100; // DIP 3
if (securityBoard.dipSwitch[2])
if (securityBoard.dipSwitch[2]) // bit8
result &= ~0x80; // DIP 2
if (securityBoard.dipSwitch[1])
if (securityBoard.dipSwitch[1]) // bit7
result &= ~0x40; // DIP 1
if (securityBoard.dipSwitch[8])
if (securityBoard.dipSwitch[8]) // bit6
result &= ~0x20;
if (securityBoard.dipSwitch[7])
if (securityBoard.dipSwitch[7]) // bit5
result &= ~0x10;
if (securityBoard.serviceSwitch) // bit4
result &= ~0x08;
if (securityBoard.testSwitch) // bit3
result &= ~0x04;
*data = result;
}
break;