From 15ad74fdad9ac10322d9500435987b5e46918e8d Mon Sep 17 00:00:00 2001 From: Saggi Mizrahi Date: Thu, 10 Jan 2013 23:49:12 -0500 Subject: [PATCH] Add retrolaunch Signed-off-by: Saggi Mizrahi --- Makefile | 17 +- tools/retrolaunch/cd_detect.c | 314 ++++++++++++++++++++++++++++ tools/retrolaunch/cd_detect.h | 3 + tools/retrolaunch/launch.conf | 243 +++++++++++++++++++++ tools/retrolaunch/log.h | 6 + tools/retrolaunch/main.c | 318 ++++++++++++++++++++++++++++ tools/retrolaunch/parser.c | 72 +++++++ tools/retrolaunch/parser.h | 6 + tools/retrolaunch/rl_fnmatch.c | 132 ++++++++++++ tools/retrolaunch/rl_fnmatch.h | 8 + tools/retrolaunch/sha1.c | 371 +++++++++++++++++++++++++++++++++ tools/retrolaunch/sha1.h | 54 +++++ 12 files changed, 1543 insertions(+), 1 deletion(-) create mode 100644 tools/retrolaunch/cd_detect.c create mode 100644 tools/retrolaunch/cd_detect.h create mode 100644 tools/retrolaunch/launch.conf create mode 100644 tools/retrolaunch/log.h create mode 100644 tools/retrolaunch/main.c create mode 100644 tools/retrolaunch/parser.c create mode 100644 tools/retrolaunch/parser.h create mode 100644 tools/retrolaunch/rl_fnmatch.c create mode 100644 tools/retrolaunch/rl_fnmatch.h create mode 100644 tools/retrolaunch/sha1.c create mode 100644 tools/retrolaunch/sha1.h diff --git a/Makefile b/Makefile index a370ae86f9..09e4aee256 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ include config.mk -TARGET = retroarch tools/retroarch-joyconfig +TARGET = retroarch tools/retroarch-joyconfig tools/retrolaunch/retrolaunch OBJ = retroarch.o \ file.o \ @@ -35,6 +35,17 @@ JOYCONFIG_OBJ = tools/retroarch-joyconfig.o \ compat/compat.o \ input/input_common.o +RETROLAUNCH_OBJ = tools/retrolaunch/main.o \ + tools/retrolaunch/sha1.o \ + tools/retrolaunch/parser.o \ + tools/retrolaunch/cd_detect.o \ + tools/retrolaunch/rl_fnmatch.o \ + file_path.o \ + compat/compat.o \ + conf/config_file.o \ + settings.o \ + $(NULL) + HEADERS = $(wildcard */*.h) $(wildcard *.h) ifeq ($(findstring Haiku,$(OS)),) @@ -339,6 +350,10 @@ else $(Q)$(CC) -o $@ $(JOYCONFIG_OBJ) $(SDL_LIBS) $(LDFLAGS) $(LIBRARY_DIRS) endif +tools/retrolaunch/retrolaunch: $(RETROLAUNCH_OBJ) + @$(if $(Q), $(shell echo echo LD $@),) + $(Q)$(LD) -o $@ $(RETROLAUNCH_OBJ) $(LIBS) $(LDFLAGS) $(LIBRARY_DIRS) + %.o: %.c config.h config.mk $(HEADERS) @$(if $(Q), $(shell echo echo CC $<),) $(Q)$(CC) $(CFLAGS) $(DEFINES) -c -o $@ $< diff --git a/tools/retrolaunch/cd_detect.c b/tools/retrolaunch/cd_detect.c new file mode 100644 index 0000000000..5806007d99 --- /dev/null +++ b/tools/retrolaunch/cd_detect.c @@ -0,0 +1,314 @@ +#include "parser.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "log.h" + +#define MAGIC_LEN 16 + +struct MagicEntry { + char* system_name; + char* magic; +}; + +static struct MagicEntry MAGIC_NUMBERS[] = { + {"ps1", "\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x02\x00\x02\x00"}, + {"pcecd", "\x82\xb1\x82\xcc\x83\x76\x83\x8d\x83\x4f\x83\x89\x83\x80\x82\xcc\x92"}, + {"scd", "\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x02\x00\x01\x53"}, + {NULL, NULL} +}; + +static int find_first_data_track(const char* cue_path, off_t* offset, + char* track_path, size_t max_len) { + int rv; + int fd = -1; + char tmp_token[MAX_TOKEN_LEN]; + int m, s, f; + char* cue_path_copy; + char* cue_dir; + cue_path_copy = strdup(cue_path); + cue_dir = dirname(cue_path_copy); + + fd = open(cue_path, O_RDONLY); + if (fd < 0) { + LOG_WARN("Could not open CUE file '%s': %s", cue_path, + strerror(errno)); + rv = -errno; + goto free_path_copy; + } + + LOG_DEBUG("Parsing CUE file '%s'...", cue_path); + + while (get_token(fd, tmp_token, MAX_TOKEN_LEN) > 0) { + if (strcmp(tmp_token, "FILE") == 0) { + get_token(fd, tmp_token, MAX_TOKEN_LEN); + snprintf(track_path, max_len, "%s/%s", + cue_dir, tmp_token); + + } else if (strcasecmp(tmp_token, "TRACK") == 0) { + get_token(fd, tmp_token, MAX_TOKEN_LEN); + get_token(fd, tmp_token, MAX_TOKEN_LEN); + if (strcasecmp(tmp_token, "AUDIO") == 0) { + continue; + } + + find_token(fd, "INDEX"); + get_token(fd, tmp_token, MAX_TOKEN_LEN); + get_token(fd, tmp_token, MAX_TOKEN_LEN); + if (sscanf(tmp_token, "%02d:%02d:%02d", &m, &s, &f) < 3) { + LOG_WARN("Error parsing time stamp '%s'", tmp_token); + return -errno; + } + *offset = ((m * 60) * (s * 75) * f) * 25; + + LOG_DEBUG("Found 1st data track on file '%s+%d'", + track_path, *offset); + + rv = 0; + goto clean; + } + } + + rv = -EINVAL; + +clean: + close(fd); +free_path_copy: + free(cue_path_copy); + return rv; +} + +static int find_ps1_canonical_name(const char* game_id, char* game_name, + size_t max_len) { + int fd; + char tmp_token[MAX_TOKEN_LEN]; + int rv = 0; + fd = open("cddb/ps1.idlst", O_RDONLY); + if (fd < 0) { + LOG_WARN("Could not open id list: %s", strerror(errno)); + return -errno; + } + + while (get_token(fd, tmp_token, MAX_TOKEN_LEN) > 0) { + if(strcasecmp(tmp_token, game_id) != 0) { + get_token(fd, tmp_token, max_len); + continue; + } + + if ((rv = get_token(fd, game_name, max_len)) < 0) { + goto clean; + } + + rv = 0; + goto clean; + } + + rv = -ENOENT; +clean: + close(fd); + return rv; +} + +static int detect_ps1_game(const char* track_path, off_t offset, + char* game_name, size_t max_len) { + int rv; + char buff[4096]; + char* pattern = "cdrom:"; + char* c; + char* id_start; + int i; + + int fd = open(track_path, O_RDONLY); + if (fd < 0) { + LOG_DEBUG("Could not open data track: %s", strerror(errno)); + return -errno; + } + + if (pread(fd, buff, 10, 0x9340) > 0) { + buff[10] = '\0'; + buff[4] = '-'; + LOG_DEBUG("Found disk label '%s'", buff); + rv = find_ps1_canonical_name(buff, game_name, max_len); + if (rv == 0) { + goto clean; + } + } + c = pattern; + while (1) { + rv = read(fd, buff, 4096); + if (rv < 0) { + rv = -errno; + goto clean; + } + + for(i = 0; i < 4096; i++) { + if (*c == buff[i]) { + c++; + } else { + c = pattern; + continue; + } + + if (*c == '\0') { + id_start = &buff[i] + 1; + *strchr(id_start, ';') = '\0'; + c = strrchr(id_start, '\\') + 1; + if (c != NULL) { + id_start = c; + } + id_start[4] = '-'; + id_start[8] = id_start[9]; + id_start[9] = id_start[10]; + id_start[10] = '\0'; + LOG_DEBUG("Found ps1 id %s", id_start); + rv = find_ps1_canonical_name(id_start, game_name, max_len); + goto clean; + } + } + } + rv = -EINVAL; +clean: + close(fd); + return rv; +} + +static int detect_system(const char* track_path, off_t offset, + char** system_name) { + int rv; + char magic[MAGIC_LEN]; + int fd; + struct MagicEntry entry; + int i; + + fd = open(track_path, O_RDONLY); + if (fd < 0) { + LOG_WARN("Could not open data track of file '%s': %s", + track_path, strerror(errno)); + rv = -errno; + goto clean; + } + + if (pread(fd, magic, MAGIC_LEN, offset) < MAGIC_LEN) { + LOG_WARN("Could not read data from file '%s' at offset %d: %s", + track_path, offset, strerror(errno)); + rv = -errno; + goto clean; + } + + LOG_DEBUG("Comparing with known magic numbers..."); + for (i = 0; MAGIC_NUMBERS[i].system_name != NULL; i++) { + if (memcmp(MAGIC_NUMBERS[i].magic, magic, MAGIC_LEN) == 0) { + *system_name = MAGIC_NUMBERS[i].system_name; + rv = 0; + goto clean; + } + } + + LOG_WARN("Could not find compatible system"); + rv = -EINVAL; +clean: + close(fd); + return rv; +} + +int find_fist_cue(const char* m3u_path, char* cue_path, size_t max_len) { + char c; + int skip = 0; + int midstream = 0; + char tmp_path[PATH_MAX]; + + int fd = open(m3u_path, O_RDONLY); + if (fd < 0) { + LOG_WARN("Could not open m3u '%s': %s", m3u_path, strerror(errno)); + return -errno; + } + + strncpy(tmp_path, m3u_path, PATH_MAX); + strcpy(cue_path, dirname(tmp_path)); + cue_path += strlen(cue_path); + cue_path[0] = '/'; + cue_path++; + + while ((read(fd, &c, 1) > 0)) { + switch (c) { + case '#': + if (!midstream) { + skip = 1; + } + break; + case '\n': + if (skip) { + skip = 0; + } else if(midstream) { + cue_path[0] = '\0'; + return 0; + } + break; + case ' ': + if (!midstream) { + break; + } + default: + if (!skip) { + midstream = 1; + cue_path[0] = c; + cue_path++; + } + } + } + + return -EINVAL; +} + +int detect_cd_game(const char* target_path, char* game_name, size_t max_len) { + char cue_path[PATH_MAX]; + char track_path[PATH_MAX]; + off_t offset; + char* system_name; + int rv; + if (strcasecmp(target_path + strlen(target_path) - 4, ".m3u") == 0) { + rv = find_fist_cue(target_path, cue_path, PATH_MAX); + if (rv < 0) { + LOG_WARN("Could not parse m3u: %s", strerror(-rv)); + return rv; + } + + } else { + strncpy(cue_path, target_path, max_len); + } + + rv = find_first_data_track(cue_path, &offset, track_path, PATH_MAX); + if (rv < 0) { + LOG_WARN("Could not find valid data track: %s", strerror(-rv)); + return rv; + } + + LOG_DEBUG("Reading 1st data track..."); + + if ((rv = detect_system(track_path, offset, &system_name)) < 0) { + return rv; + } + + + LOG_DEBUG("Detected %s media", system_name); + + snprintf(game_name, max_len, "%s.", system_name); + game_name += strlen(system_name) + 1; + max_len -= strlen(system_name) + 1; + if (strcmp(system_name, "ps1") == 0) { + if (detect_ps1_game(track_path, offset, game_name, max_len) == 0) { + return 0; + } + } + + snprintf(game_name, max_len, "", system_name); + return 0; +} diff --git a/tools/retrolaunch/cd_detect.h b/tools/retrolaunch/cd_detect.h new file mode 100644 index 0000000000..a59305ff6a --- /dev/null +++ b/tools/retrolaunch/cd_detect.h @@ -0,0 +1,3 @@ +#include + +int detect_cd_game(const char* cue_path, char* game_name, size_t max_len); diff --git a/tools/retrolaunch/launch.conf b/tools/retrolaunch/launch.conf new file mode 100644 index 0000000000..64e0d44243 --- /dev/null +++ b/tools/retrolaunch/launch.conf @@ -0,0 +1,243 @@ +# PS1 Dualanalog Games ; + +"ps1.007 Racing*" mednafen-psx dualanalog ; +"ps1.007 - The World Is Not Enough*" mednafen-psx dualanalog ; +"ps1.007 - Tomorrow Never Dies*" mednafen-psx dualanalog ; +"ps1.40 Winks*" mednafen-psx dualanalog ; +"ps1.Ace Combat*" mednafen-psx dualanalog ; +"ps1.Aces of the Air*" mednafen-psx dualanalog ; +"ps1.Action Bass*" mednafen-psx dualanalog ; +"ps1.Action Man - Operation Extreme*" mednafen-psx dualanalog ; +"ps1.Advanced V.G. 2*" mednafen-psx dualanalog ; +"ps1.Aironauts*" mednafen-psx dualanalog ; +"ps1.Air Race Championship*" mednafen-psx dualanalog ; +"ps1.Akuji - The Heartless*" mednafen-psx dualanalog ; +"ps1.Alfred Chicken*" mednafen-psx dualanalog ; +"ps1.Alien la Resurrection*" mednafen-psx dualanalog ; +"ps1.Alien Resurrection*" mednafen-psx dualanalog ; +"ps1.Alone In The Dark - The New Nightmare*" mednafen-psx dualanalog ; +"ps1.Alundra 2*" mednafen-psx dualanalog ; +"ps1.Amazing Virtual Sea Monkeys*" mednafen-psx dualanalog ; +"ps1.Animorphs - Shattered Reality*" mednafen-psx dualanalog ; +"ps1.Ape Escape*" mednafen-psx dualanalog ; +"ps1.Apocalypse*" mednafen-psx dualanalog ; +"ps1.Aqua GT*" mednafen-psx dualanalog ; +"ps1.Armored Core - Master of Arena*" mednafen-psx dualanalog ; +"ps1.Armorines - Project Swarm*" mednafen-psx dualanalog ; +"ps1.Army Men 3D*" mednafen-psx dualanalog ; +"ps1.Army Men - Air Attack 2*" mednafen-psx dualanalog ; +"ps1.Army Men - Air Attack*" mednafen-psx dualanalog ; +"ps1.Army Men - Green Rouge*" mednafen-psx dualanalog ; +"ps1.Army Men - Sarge's Heroes 2*" mednafen-psx dualanalog ; +"ps1.Army Men - Sarge's Heroes*" mednafen-psx dualanalog ; +"ps1.Army Men - World War*" mednafen-psx dualanalog ; +"ps1.Assault Retribution*" mednafen-psx dualanalog ; +"ps1.Asterix - Mega Madness*" mednafen-psx dualanalog ; +"ps1.Asteroids*" mednafen-psx dualanalog ; +"ps1.Barbie Explorer" mednafen-psx dualanalog ; +"ps1.Barbie - Gotta Have Games" mednafen-psx dualanalog ; +"ps1.Batman Beyond - Return of the Joker" mednafen-psx dualanalog ; +"ps1.Batman & Robin" mednafen-psx dualanalog ; +"ps1.Beatmania" mednafen-psx dualanalog ; +"ps1.Beat Planet Music" mednafen-psx dualanalog ; +"ps1.Beyblade" mednafen-psx dualanalog ; +"ps1.Big Bass Fishing" mednafen-psx dualanalog ; +"ps1.BioHazard - Director's Cut - Dual Shock*" mednafen-psx dualanalog ; +"ps1.Bishi Bashi Special" mednafen-psx dualanalog ; +"ps1.Blade" mednafen-psx dualanalog ; +"ps1.Blasto!" mednafen-psx dualanalog ; +"ps1.Blast Radius" mednafen-psx dualanalog ; +"ps1.Block Kuzushi" mednafen-psx dualanalog ; +"ps1.Block Wars" mednafen-psx dualanalog ; +"ps1.Bloody Roar 2*" mednafen-psx dualanalog ; +"ps1.Bob the Builder - Can We Fix It" mednafen-psx dualanalog ; +"ps1.Boombots" mednafen-psx dualanalog ; +"ps1.Bratz" mednafen-psx dualanalog ; +"ps1.Brave Fencer Musashi*" mednafen-psx dualanalog ; +"ps1.Breakout" mednafen-psx dualanalog ; +"ps1.Buggy" mednafen-psx dualanalog ; +"ps1.Bugs Bunny - Lost in Time*" mednafen-psx dualanalog ; +"ps1.Bugs Bunny & Taz - Time Busters" mednafen-psx dualanalog ; +"ps1.C-12 - The Final Resistance" mednafen-psx dualanalog ; +"ps1.Captain Commando" mednafen-psx dualanalog ; +"ps1.Cardinal Syn*" mednafen-psx dualanalog ; +"ps1.Carmageddon*" mednafen-psx dualanalog ; +"ps1.Casper - Friends Around The World" mednafen-psx dualanalog ; +"ps1.Chaos Break" mednafen-psx dualanalog ; +"ps1.Chicken Run" mednafen-psx dualanalog ; +"ps1.Chippoke Ralph no Daibouken (Adventure of Little Ralph)" mednafen-psx dualanalog ; +"ps1.Chocobo Racing" mednafen-psx dualanalog ; +"ps1.Chrono Cross*" pcsxr dualanalog ; +"ps1.Colin Mc[rR]rae*" mednafen-psx dualanalog ; +"ps1.Colony Wars*" mednafen-psx dualanalog ; +"ps1.Cosmowarrior Rei" mednafen-psx dualanalog ; +"ps1.Cowboy Bebop" mednafen-psx dualanalog ; +"ps1.Crash Bandicoot 2*" mednafen-psx dualanalog ; +"ps1.Crash Bandicoot 3*" mednafen-psx dualanalog ; +"ps1.Crash Bandicoot Carnival - Crash Bash*" mednafen-psx dualanalog ; +"ps1.Crash Bash Demo" mednafen-psx dualanalog ; +"ps1.Crash Bash" mednafen-psx dualanalog ; +"ps1.Crash Bash" mednafen-psx dualanalog ; +"ps1.Crash Bash & Spyro - Year of the Dragon Demo" mednafen-psx dualanalog ; +"ps1.Crash Team Racing*" mednafen-psx dualanalog multitap ; +"ps1.Crisis Beat" mednafen-psx dualanalog ; +"ps1.Croc 2*" mednafen-psx dualanalog ; +"ps1.Croc - Legend of the Gobbos" mednafen-psx dualanalog ; +"ps1.C - The Contra Adventure" mednafen-psx dualanalog ; +"ps1.CT Special Forces" mednafen-psx dualanalog ; +"ps1.CyberTiger Golf" mednafen-psx dualanalog ; +"ps1.Fighting Force*" mednafen-psx dualanalog ; +"ps1.Future Cop*" mednafen-psx dualanalog ; +"ps1.Legacy of Kain - Soul Reaver*" pcsxr dualanalog ; +"ps1.Saru! Get You!*" mednafen-psx dualanalog ; +"ps1.Simple Characters 2000 Series #02 - Afro Inu - The Puzzle*" mednafen-psx dualanalog ; +"ps1.Soul Reaver - Legacy of Kain*" mednafen-psx dualanalog ; +"ps1.Spyro the Dragon*"" mednafen-psx dualanalog ; +"ps1.Spyro - Year of the Dragon*" mednafen-psx dualanalog ; +"ps1.Vagrant Story*" mednafen-psx dualanalog ; + +"ps1.Earthworm Jim 2" pcsxr ; +"ps1.Mickey's Wild Adventures" pcsxr ; + +# SNES BSNES Performance ; +"snes. + +"ps1.Earthworm Jim 2" pcsxr ; +"ps1.Mickey's Wild Adventures" pcsxr ; + +# SNES BSNES Performance ; +"snes.Psycho Dream*" bsnes-performance ; + +# SNES Multitap Games ; + +"snes.Bakukyuu Renpatsu!! Super B-Daman*" snes9x multitap ; +"snes.Bakutou Dochers - Bumps-jima wa Oosawagi*" snes9x multitap ; +"snes.Barkley Shut Up and Jam!*" snes9x multitap ; +"snes.Barkley Shut Up and Jam!*" snes9x multitap ; +"snes.Battle Cross*" snes9x multitap ; +"snes.Battle Jockey*" snes9x multitap ; +"snes.Bill Walsh College Football*" snes9x multitap ; +"snes.Capcom's Soccer Shootout*" snes9x multitap ; +"snes.College Slam*" snes9x multitap ; +"snes.Crystal Beans From Dungeon Explorer*" snes9x multitap ; +"snes.Dragon - The Bruce Lee Story*" snes9x multitap ; +"snes.Dream Basketball - Dunk and Hoop*" snes9x multitap ; +"snes.Dynamic Stadium*" snes9x multitap ; +"snes.ESPN National Hockey Night*" snes9x multitap ; +"snes.FIFA 98*" snes9x multitap ; +"snes.FIFA International Soccer*" snes9x multitap ; +"snes.FIFA Soccer 96*" snes9x multitap ; +"snes.FIFA Soccer 97*" snes9x multitap ; +"snes.Final Set*" snes9x multitap ; +"snes.Fire Striker*" snes9x multitap ; +"snes.From TV Animation Slam Dunk - SD Heat Up!!*" snes9x multitap ; +"snes.Go! Go! Dodge League*" snes9x multitap ; +"snes.Hammerlock Wrestling*" snes9x multitap ; +"snes.Hat Trick Hero 2*" snes9x multitap ; +"snes.Head-On Soccer*" snes9x multitap ; +"snes.Hebereke no Oishii Puzzle ha Irimasenka*" snes9x multitap ; +"snes.Human Grand Prix III - F1 Triple Battle*" snes9x multitap ; +"snes.Human Grand Prix IV - F1 Dream Battle*" snes9x multitap ; +"snes.Hungry Dinosaurs*" snes9x multitap ; +"snes.International Superstar Soccer Deluxe*" snes9x multitap ; +"snes.J.League Excite Stage '94*" snes9x multitap ; +"snes.J.League Excite Stage '95*" snes9x multitap ; +"snes.J.League Excite Stage '96*" snes9x multitap ; +"snes.J.League Super Soccer '95*" snes9x multitap ; +"snes.J.League Super Soccer*" snes9x multitap ; +"snes.JWP Joshi Pro Wrestling - Pure Wrestle Queens*" snes9x multitap ; +"snes.Jikkyou Power Pro Wrestling '96*" snes9x multitap ; +"snes.Jimmy Connors Pro Tennis Tour*" snes9x multitap ; +"snes.Kunio-kun no Dodge Ball dayo Zenin Shuugou!*" snes9x multitap ; +"snes.Looney Tunes Basketball*" snes9x multitap ; +"snes.Madden NFL '94*" snes9x multitap ; +"snes.Madden NFL '95*" snes9x multitap ; +"snes.Madden NFL '96*" snes9x multitap ; +"snes.Madden NFL '97*" snes9x multitap ; +"snes.Madden NFL '98*" snes9x multitap ; +"snes.Micro Machines 2 - Turbo Tournament*" snes9x multitap ; +"snes.Micro Machines*" snes9x multitap ; +"snes.Mizuki Shigeru no Youkai Hyakkiyakou*" snes9x multitap ; +"snes.Multi Play Volleyball*" snes9x multitap ; +"snes.NBA Give 'N Go*" snes9x multitap ; +"snes.NBA Hang Time*" snes9x multitap ; +"snes.NBA Jam - Tournament Edition*" snes9x multitap ; +"snes.NBA Jam*" snes9x multitap ; +"snes.NBA Live 95*" snes9x multitap ; +"snes.NBA Live 96*" snes9x multitap ; +"snes.NBA Live 97*" snes9x multitap ; +"snes.NBA Live 98*" snes9x multitap ; +"snes.NCAA Final Four Basketball*" snes9x multitap ; +"snes.NCAA Football*" snes9x multitap ; +"snes.NFL Quarterback Club 96*" snes9x multitap ; +"snes.NFL Quarterback Club*" snes9x multitap ; +"snes.NHL '94*" snes9x multitap ; +"snes.NHL '98*" snes9x multitap ; +"snes.NHL Pro Hockey '94*" snes9x multitap ; +"snes.Natsume Championship Wrestling*" snes9x multitap ; +"snes.Peace Keepers, The*" snes9x multitap ; +"snes.Pieces*" snes9x multitap ; +"snes.Rap Jam - Volume One*" snes9x multitap ; +"snes.Saturday Night Slam Masters*" snes9x multitap ; +"snes.Secret of Mana*" snes9x multitap ; +"snes.Shin Nippon Pro Wrestling '94 - Battlefield in Tokyo Dome*" snes9x multitap ; +"snes.Shin Nippon Pro Wrestling - Chou Senshi in Tokyo Dome*" snes9x multitap ; +"snes.Shin Nippon Pro Wrestling Kounin '95 - Tokyo Dome Battle 7*" snes9x multitap ; +"snes.Smash Tennis*" snes9x multitap ; +"snes.Sporting News, The - Power Baseball*" snes9x multitap ; +"snes.Sterling Sharpe End 2 End*" snes9x multitap ; +"snes.Street Hockey '95*" snes9x multitap ; +"snes.Street Racer*" snes9x multitap ; +"snes.Sugoi Hebereke*" snes9x multitap ; +"snes.Sugoro Quest++ Dicenics*" snes9x multitap ; +"snes.Super Bomberman - Panic Bomber W*" snes9x multitap ; +"snes.Super Bomberman 2*" snes9x multitap ; +"snes.Super Bomberman 3*" snes9x multitap ; +"snes.Super Bomberman 4*" snes9x multitap ; +"snes.Super Bomberman 5*" snes9x multitap ; +"snes.Super Bomberman*" snes9x multitap ; +"snes.Super Fire Pro Wrestling - Queen's Special*" snes9x multitap ; +"snes.Super Fire Pro Wrestling Special*" snes9x multitap ; +"snes.Super Fire Pro Wrestling X Premium*" snes9x multitap ; +"snes.Super Fire Pro Wrestling X*" snes9x multitap ; +"snes.Super Formation Soccer 94 - World Cup Final Data*" snes9x multitap ; +"snes.Super Formation Soccer 94*" snes9x multitap ; +"snes.Super Formation Soccer 95 della Serie A - UCC Xaqua*" snes9x multitap ; +"snes.Super Formation Soccer 95 della Serie A*" snes9x multitap ; +"snes.Super Formation Soccer 96*" snes9x multitap ; +"snes.Super Formation Soccer II*" snes9x multitap ; +"snes.Super Ice Hockey*" snes9x multitap ; +"snes.Super Kyousouba - Kaze no Sylphid*" snes9x multitap ; +"snes.Super Power League*" snes9x multitap ; +"snes.Super Tekkyuu Fight!*" snes9x multitap ; +"snes.Super Tetris 3*" snes9x multitap ; +"snes.Syndicate*" snes9x multitap ; +"snes.Tenryu Genichiro no Pro Wrestling Revolution*" snes9x multitap ; +"snes.Tiny Toon Adventures - Wild & Wacky Sports*" snes9x multitap ; +"snes.Top Gear 3000*" snes9x multitap ; +"snes.Turbo Toons*" snes9x multitap ; +"snes.Virtual Soccer*" snes9x multitap ; +"snes.Vs. Collection*" snes9x multitap ; +"snes.WWF Raw*" snes9x multitap ; +"snes.Yuujin no Furi Furi Girls*" snes9x multitap ; +"snes.Zero 4 Champ RR-Z*" snes9x multitap ; +"snes.Zero 4 Champ RR*" snes9x multitap ; + +# Defaults ; + +"gba.*" vba ; +"gbc.*" gambatte ; +"gb.*" gambatte ; +"gg.*" genplus ; +"nes.*" fceu ; +"pcecd.*" mednafen-pce-fast ; +"pce.*" mednafen-pce-fast ; +"ps1.*" mednafen-psx ; +"wswan.*" mednafen-wswan ; +"a26.*" stella ; +"scd.*" genplus ; +"sg1000.*" genplus ; +"smd.*" genplus ; +"sms.*" genplus ; +"snes.*" snes9x ; +"nds.*" desmume ; diff --git a/tools/retrolaunch/log.h b/tools/retrolaunch/log.h new file mode 100644 index 0000000000..fd20988c25 --- /dev/null +++ b/tools/retrolaunch/log.h @@ -0,0 +1,6 @@ +#include + +#define LOG(stream, level, msg, ...) fprintf(stream, "%s::%s+%d::", level, __FILE__, __LINE__); fprintf(stream, msg, ##__VA_ARGS__); fprintf(stream, "\n") +#define LOG_DEBUG(msg, ...) LOG(stderr, "DEBUG", msg, ##__VA_ARGS__) +#define LOG_WARN(msg, ...) LOG(stderr, "WARNING", msg, ##__VA_ARGS__) +#define LOG_INFO(msg, ...) LOG(stdout, "INFO", msg, ##__VA_ARGS__) diff --git a/tools/retrolaunch/main.c b/tools/retrolaunch/main.c new file mode 100644 index 0000000000..e91fc18236 --- /dev/null +++ b/tools/retrolaunch/main.c @@ -0,0 +1,318 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sha1.h" +#include "parser.h" +#include "cd_detect.h" +#include "rl_fnmatch.h" +#include "../../file.h" + +#include "log.h" + +#define SHA1_LEN 40 +#define HASH_LEN SHA1_LEN + +static int find_hash(int fd, const char *hash, char *game_name, size_t max_len) +{ + char token[MAX_TOKEN_LEN]; + while (1) { + if (find_token(fd, "game") < 0) { + return -1; + } + + if (find_token(fd, "name") < 0) { + return -1; + } + + if (get_token(fd, game_name, max_len) < 0) { + return -1; + } + + if (find_token(fd, "sha1") < 0) { + return -1; + } + + if (get_token(fd, token, MAX_TOKEN_LEN) < 0) { + return -1; + } + + if (strcasecmp(hash, token) == 0) { + return 0; + } + } +} + +static int +find_rom_canonical_name(const char *hash, char *game_name, size_t max_len) +{ + // TODO: Error handling + int i; + int rv; + int fd; + int offs; + char *dat_path; + char *dat_name; + struct string_list *files; + + files = dir_list_new("db", "dat", false); + if (!files) { + return -1; + } + + for (i = 0; i < files->size; i++) { + dat_path = files->elems[i].data; + dat_name = basename(dat_path); + offs = strchr(dat_name, '.') - dat_name + 1; + memcpy(game_name, dat_name, offs); + + fd = open(dat_path, O_RDONLY); + if (fd < 0) { + continue; + } + + if (find_hash(fd, hash, game_name + offs, max_len - offs) == 0) { + rv = 0; + close(fd); + goto clean; + } + close(fd); + } + rv = -1; +clean: + dir_list_free(files); + return rv; +} + +static int get_sha1(const char *path, char *result) +{ + int fd; + int rv; + int buff_len = 4096; + unsigned char buff[buff_len]; + SHA1Context sha; + + fd = open(path, O_RDONLY); + if (fd < 0) { + return -errno; + } + + SHA1Reset(&sha); + rv = 1; + while (rv > 0) { + rv = read(fd, buff, buff_len); + if (rv < 0) { + close(fd); + return -errno; + } + + SHA1Input(&sha, buff, rv); + } + + if (!SHA1Result(&sha)) { + return -1; + } + + sprintf(result, "%08X%08X%08X%08X%08X", + sha.Message_Digest[0], + sha.Message_Digest[1], + sha.Message_Digest[2], + sha.Message_Digest[3], sha.Message_Digest[4]); + return 0; +} + +struct RunInfo { + char core[50]; + int multitap; + int dualanalog; +}; + +static int get_run_info(struct RunInfo *info, char *game_name) +{ + int fd = open("./launch.conf", O_RDONLY); + int rv; + char token[MAX_TOKEN_LEN]; + if (fd < 0) { + return -errno; + } + + memset(info, 0, sizeof(struct RunInfo)); + + while (1) { + if ((rv = get_token(fd, token, MAX_TOKEN_LEN)) < 0) { + goto clean; + } + + if (rl_fnmatch(token, game_name, 0) != 0) { + if ((rv = find_token(fd, ";")) < 0) { + goto clean; + } + continue; + } + + LOG_DEBUG("Matched rule '%s'", token); + + if ((rv = get_token(fd, token, MAX_TOKEN_LEN)) < 0) { + goto clean; + } + + break; + } + + strncpy(info->core, token, 50); + info->multitap = 0; + info->dualanalog = 0; + + if ((rv = get_token(fd, token, MAX_TOKEN_LEN)) < 0) { + goto clean; + } + + while (strcmp(token, ";") != 0) { + if (strcmp(token, "multitap") == 0) { + info->multitap = 1; + } else if (strcmp(token, "dualanalog") == 0) { + info->dualanalog = 1; + } + + if ((rv = get_token(fd, token, MAX_TOKEN_LEN)) < 0) { + goto clean; + } + + } + rv = 0; + clean: + close(fd); + return rv; +} + +char *SUFFIX_MATCH[] = { + ".a26", "a26", + ".bin", "smd", + ".gba", "gba", + ".gbc", "gbc", + ".gb", "gb", + ".gen", "smd", + ".gg", "gg", + ".nds", "nds", + ".nes", "nes", + ".pce", "pce", + ".sfc", "snes", + ".smc", "snes", + ".smd", "smd", + ".sms", "sms", + ".wsc", "wswan", + NULL +}; + +static int detect_rom_game(const char *path, char *game_name, size_t max_len) +{ + char hash[HASH_LEN + 1]; + int rv; + char *suffix = strrchr(path, '.'); + char **tmp_suffix; + if ((rv = get_sha1(path, hash)) < 0) { + LOG_WARN("Could not calculate hash: %s", strerror(-rv)); + } + + if (find_rom_canonical_name(hash, game_name, max_len) < 0) { + LOG_DEBUG("Could not detect rom with hash `%s` guessing", hash); + + for (tmp_suffix = SUFFIX_MATCH; *tmp_suffix != NULL; + tmp_suffix += 2) { + if (strcasecmp(suffix, *tmp_suffix) == 0) { + snprintf(game_name, max_len, "%s.", + *(tmp_suffix + 1)); + return 0; + } + } + return -EINVAL; + } + + return 0; +} + +static int detect_game(const char *path, char *game_name, size_t max_len) +{ + if ((strcasecmp(path + strlen(path) - 4, ".cue") == 0) || + (strcasecmp(path + strlen(path) - 4, ".m3u") == 0)) { + LOG_INFO("Starting CD game detection..."); + return detect_cd_game(path, game_name, max_len); + } else { + LOG_INFO("Starting rom game detection..."); + return detect_rom_game(path, game_name, max_len); + } +} + +static int run_retroarch(const char *path, const struct RunInfo *info) +{ + char core_path[PATH_MAX]; + sprintf(core_path, "./cores/libretro-%s.so", info->core); + char *retro_argv[30] = { "retroarch", + "-L", core_path + }; + int argi = 3; + if (info->multitap) { + retro_argv[argi] = "-4"; + argi++; + LOG_INFO("Game supports multitap"); + } + + if (info->dualanalog) { + retro_argv[argi] = "-A"; + argi++; + retro_argv[argi] = "1"; + argi++; + retro_argv[argi] = "-A"; + argi++; + retro_argv[argi] = "2"; + argi++; + LOG_INFO("Game supports the dualshock controller"); + } + + retro_argv[argi] = strdup(path); + argi++; + retro_argv[argi] = NULL; + execvp(retro_argv[0], retro_argv); + return -errno; +} + +int main(int argc, char *argv[]) +{ + if (argc < 2) { + return -1; + } + + char game_name[MAX_TOKEN_LEN]; + char *path = argv[1]; + struct RunInfo info; + int rv; + + LOG_INFO("Analyzing '%s'", path); + if ((rv = detect_game(path, game_name, MAX_TOKEN_LEN)) < 0) { + LOG_WARN("Could not detect game: %s", strerror(-rv)); + return -rv; + } + + LOG_INFO("Game is `%s`", game_name); + if ((rv = get_run_info(&info, game_name)) < 0) { + LOG_WARN("Could not find sutable core: %s", strerror(-rv)); + return -1; + } + + LOG_DEBUG("Usinge libretro core '%s'", info.core); + LOG_INFO("Launching '%s'", path); + + rv = run_retroarch(path, &info); + LOG_WARN("Could not launch retroarch: %s", strerror(-rv)); + return -rv; +} + +// Stub just so that it compiles +void rarch_init_msg_queue(void) {} diff --git a/tools/retrolaunch/parser.c b/tools/retrolaunch/parser.c new file mode 100644 index 0000000000..b7f135db69 --- /dev/null +++ b/tools/retrolaunch/parser.c @@ -0,0 +1,72 @@ +#include "parser.h" + +#include +#include +#include + +ssize_t get_token(int fd, char *token, size_t max_len) +{ + char *c = token; + int rv; + ssize_t len = 0; + int in_string = 0; + + while (1) { + rv = read(fd, c, 1); + if (rv == 0) { + return 0; + } else if (rv < 1) { + switch (errno) { + case EINTR: + case EAGAIN: + continue; + default: + return -errno; + } + } + + switch (*c) { + case ' ': + case '\t': + case '\r': + case '\n': + if (c == token) { + continue; + } + + if (!in_string) { + *c = '\0'; + return len; + } + break; + case '\"': + if (c == token) { + in_string = 1; + continue; + } + + *c = '\0'; + return len; + } + + len++; + c++; + if (len == max_len) { + *c = '\0'; + return len; + } + } +} + +int find_token(int fd, char *token) +{ + int tmp_len = strlen(token); + char *tmp_token = calloc(tmp_len, sizeof(char)); + while (strncmp(tmp_token, token, tmp_len) != 0) { + if (get_token(fd, tmp_token, tmp_len) <= 0) { + return -1; + } + } + + return 0; +} diff --git a/tools/retrolaunch/parser.h b/tools/retrolaunch/parser.h new file mode 100644 index 0000000000..195fb3259a --- /dev/null +++ b/tools/retrolaunch/parser.h @@ -0,0 +1,6 @@ +#include + +#define MAX_TOKEN_LEN 255 + +ssize_t get_token(int fd, char* token, size_t max_len); +int find_token(int fd, char* token); diff --git a/tools/retrolaunch/rl_fnmatch.c b/tools/retrolaunch/rl_fnmatch.c new file mode 100644 index 0000000000..604ea74b18 --- /dev/null +++ b/tools/retrolaunch/rl_fnmatch.c @@ -0,0 +1,132 @@ +#if __TEST_FNMATCH__ +#include +#endif + +#include "rl_fnmatch.h" + +// Implemnentation of fnmatch(3) so it can be distributed to non *nix platforms +// No flags are implemented ATM. We don't use them. Add flags as needed. +int rl_fnmatch(const char *pattern, const char *string, int flags) { + const char *c; + int charmatch = 0; + int rv; + for (c = pattern; *c != '\0'; c++) { + // String ended before pattern + if ((*c != '*') && (*string == '\0')) { + return FNM_NOMATCH; + } + + switch (*c) { + // Match any number of unknown chars + case '*': + // Find next node in the pattern ignoring multiple + // asterixes + do { + c++; + if (*c == '\0') { + return 0; + } + } while (*c == '*'); + + // Match the remaining pattern ingnoring more and more + // chars. + do { + // We reached the end of the string without a + // match. There is a way to optimize this by + // calculating the minimum chars needed to + // match the remaining pattern but I don't + // think it is worth the work ATM. + if (*string == '\0') { + return FNM_NOMATCH; + } + + rv = rl_fnmatch(c, string, flags); + string++; + } while (rv != 0); + + return 0; + // Match char from list + case '[': + charmatch = 0; + for (c++; *c != ']'; c++) { + // Bad formath + if (*c == '\0') { + return FNM_NOMATCH; + } + + // Match already found + if (charmatch) { + continue; + } + + if (*c == *string) { + charmatch = 1; + } + } + + // No match in list + if (!charmatch) { + return FNM_NOMATCH; + } + + string++; + break; + // Has any char + case '?': + string++; + break; + // Match following char verbatim + case '\\': + c++; + // Dangling escape at end of pattern + if (c == '\0') { + return FNM_NOMATCH; + } + default: + if (*c != *string) { + return FNM_NOMATCH; + } + string++; + } + } + + // End of string and end of pattend + if (*string == '\0') { + return 0; + } else { + return FNM_NOMATCH; + } +} + +#if __TEST_FNMATCH__ +int main() { + assert(rl_fnmatch("TEST", "TEST", 0) == 0); + assert(rl_fnmatch("TE?T", "TEST", 0) == 0); + assert(rl_fnmatch("TE[Ssa]T", "TEST", 0) == 0); + assert(rl_fnmatch("TE[Ssda]T", "TEsT", 0) == 0); + assert(rl_fnmatch("TE[Ssda]T", "TEdT", 0) == 0); + assert(rl_fnmatch("TE[Ssda]T", "TEaT", 0) == 0); + assert(rl_fnmatch("TEST*", "TEST", 0) == 0); + assert(rl_fnmatch("TEST**", "TEST", 0) == 0); + assert(rl_fnmatch("TE*ST*", "TEST", 0) == 0); + assert(rl_fnmatch("TE**ST*", "TEST", 0) == 0); + assert(rl_fnmatch("TE**ST*", "TExST", 0) == 0); + assert(rl_fnmatch("TE**ST", "TEST", 0) == 0); + assert(rl_fnmatch("TE**ST", "TExST", 0) == 0); + assert(rl_fnmatch("TE\\**ST", "TE*xST", 0) == 0); + assert(rl_fnmatch("*.*", "test.jpg", 0) == 0); + assert(rl_fnmatch("*.jpg", "test.jpg", 0) == 0); + assert(rl_fnmatch("*.[Jj][Pp][Gg]", "test.jPg", 0) == 0); + assert(rl_fnmatch("*.[Jj]*[Gg]", "test.jPg", 0) == 0); + assert(rl_fnmatch("TEST?", "TEST", 0) == FNM_NOMATCH); + assert(rl_fnmatch("TES[asd", "TEST", 0) == FNM_NOMATCH); + assert(rl_fnmatch("TEST\\", "TEST", 0) == FNM_NOMATCH); + assert(rl_fnmatch("TEST*S", "TEST", 0) == FNM_NOMATCH); + assert(rl_fnmatch("TE**ST", "TExT", 0) == FNM_NOMATCH); + assert(rl_fnmatch("TE\\*T", "TExT", 0) == FNM_NOMATCH); + assert(rl_fnmatch("TES?", "TES", 0) == FNM_NOMATCH); + assert(rl_fnmatch("TE", "TEST", 0) == FNM_NOMATCH); + assert(rl_fnmatch("TEST!", "TEST", 0) == FNM_NOMATCH); + assert(rl_fnmatch("DSAD", "TEST", 0) == FNM_NOMATCH); +} +#endif diff --git a/tools/retrolaunch/rl_fnmatch.h b/tools/retrolaunch/rl_fnmatch.h new file mode 100644 index 0000000000..43895ccdb1 --- /dev/null +++ b/tools/retrolaunch/rl_fnmatch.h @@ -0,0 +1,8 @@ +#ifndef __RL_FNMATCH_H__ +#define __RL_FNMATCH_H__ + +#define FNM_NOMATCH 1 + +int rl_fnmatch(const char *pattern, const char *string, int flags); + +#endif diff --git a/tools/retrolaunch/sha1.c b/tools/retrolaunch/sha1.c new file mode 100644 index 0000000000..d87c7f4830 --- /dev/null +++ b/tools/retrolaunch/sha1.c @@ -0,0 +1,371 @@ +/* + * sha1.c + * + * Copyright (C) 1998, 2009 + * Paul E. Jones + * All Rights Reserved + * + ***************************************************************************** + * $Id: sha1.c 12 2009-06-22 19:34:25Z paulej $ + ***************************************************************************** + * + * Description: + * This file implements the Secure Hashing Standard as defined + * in FIPS PUB 180-1 published April 17, 1995. + * + * The Secure Hashing Standard, which uses the Secure Hashing + * Algorithm (SHA), produces a 160-bit message digest for a + * given data stream. In theory, it is highly improbable that + * two messages will produce the same message digest. Therefore, + * this algorithm can serve as a means of providing a "fingerprint" + * for a message. + * + * Portability Issues: + * SHA-1 is defined in terms of 32-bit "words". This code was + * written with the expectation that the processor has at least + * a 32-bit machine word size. If the machine word size is larger, + * the code should still function properly. One caveat to that + * is that the input functions taking characters and character + * arrays assume that only 8 bits of information are stored in each + * character. + * + * Caveats: + * SHA-1 is designed to work with messages less than 2^64 bits + * long. Although SHA-1 allows a message digest to be generated for + * messages of any number of bits less than 2^64, this + * implementation only works with messages with a length that is a + * multiple of the size of an 8-bit character. + * + */ + +#include "sha1.h" + +/* + * Define the circular shift macro + */ +#define SHA1CircularShift(bits,word) \ + ((((word) << (bits)) & 0xFFFFFFFF) | \ + ((word) >> (32-(bits)))) + +/* Function prototypes */ +void SHA1ProcessMessageBlock(SHA1Context *); +void SHA1PadMessage(SHA1Context *); + +/* + * SHA1Reset + * + * Description: + * This function will initialize the SHA1Context in preparation + * for computing a new message digest. + * + * Parameters: + * context: [in/out] + * The context to reset. + * + * Returns: + * Nothing. + * + * Comments: + * + */ +void SHA1Reset(SHA1Context *context) +{ + context->Length_Low = 0; + context->Length_High = 0; + context->Message_Block_Index = 0; + + context->Message_Digest[0] = 0x67452301; + context->Message_Digest[1] = 0xEFCDAB89; + context->Message_Digest[2] = 0x98BADCFE; + context->Message_Digest[3] = 0x10325476; + context->Message_Digest[4] = 0xC3D2E1F0; + + context->Computed = 0; + context->Corrupted = 0; +} + +/* + * SHA1Result + * + * Description: + * This function will return the 160-bit message digest into the + * Message_Digest array within the SHA1Context provided + * + * Parameters: + * context: [in/out] + * The context to use to calculate the SHA-1 hash. + * + * Returns: + * 1 if successful, 0 if it failed. + * + * Comments: + * + */ +int SHA1Result(SHA1Context *context) +{ + + if (context->Corrupted) + { + return 0; + } + + if (!context->Computed) + { + SHA1PadMessage(context); + context->Computed = 1; + } + + return 1; +} + +/* + * SHA1Input + * + * Description: + * This function accepts an array of octets as the next portion of + * the message. + * + * Parameters: + * context: [in/out] + * The SHA-1 context to update + * message_array: [in] + * An array of characters representing the next portion of the + * message. + * length: [in] + * The length of the message in message_array + * + * Returns: + * Nothing. + * + * Comments: + * + */ +void SHA1Input( SHA1Context *context, + const unsigned char *message_array, + unsigned length) +{ + if (!length) + { + return; + } + + if (context->Computed || context->Corrupted) + { + context->Corrupted = 1; + return; + } + + while(length-- && !context->Corrupted) + { + context->Message_Block[context->Message_Block_Index++] = + (*message_array & 0xFF); + + context->Length_Low += 8; + /* Force it to 32 bits */ + context->Length_Low &= 0xFFFFFFFF; + if (context->Length_Low == 0) + { + context->Length_High++; + /* Force it to 32 bits */ + context->Length_High &= 0xFFFFFFFF; + if (context->Length_High == 0) + { + /* Message is too long */ + context->Corrupted = 1; + } + } + + if (context->Message_Block_Index == 64) + { + SHA1ProcessMessageBlock(context); + } + + message_array++; + } +} + +/* + * SHA1ProcessMessageBlock + * + * Description: + * This function will process the next 512 bits of the message + * stored in the Message_Block array. + * + * Parameters: + * None. + * + * Returns: + * Nothing. + * + * Comments: + * Many of the variable names in the SHAContext, especially the + * single character names, were used because those were the names + * used in the publication. + * + * + */ +void SHA1ProcessMessageBlock(SHA1Context *context) +{ + const unsigned K[] = /* Constants defined in SHA-1 */ + { + 0x5A827999, + 0x6ED9EBA1, + 0x8F1BBCDC, + 0xCA62C1D6 + }; + int t; /* Loop counter */ + unsigned temp; /* Temporary word value */ + unsigned W[80]; /* Word sequence */ + unsigned A, B, C, D, E; /* Word buffers */ + + /* + * Initialize the first 16 words in the array W + */ + for(t = 0; t < 16; t++) + { + W[t] = ((unsigned) context->Message_Block[t * 4]) << 24; + W[t] |= ((unsigned) context->Message_Block[t * 4 + 1]) << 16; + W[t] |= ((unsigned) context->Message_Block[t * 4 + 2]) << 8; + W[t] |= ((unsigned) context->Message_Block[t * 4 + 3]); + } + + for(t = 16; t < 80; t++) + { + W[t] = SHA1CircularShift(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]); + } + + A = context->Message_Digest[0]; + B = context->Message_Digest[1]; + C = context->Message_Digest[2]; + D = context->Message_Digest[3]; + E = context->Message_Digest[4]; + + for(t = 0; t < 20; t++) + { + temp = SHA1CircularShift(5,A) + + ((B & C) | ((~B) & D)) + E + W[t] + K[0]; + temp &= 0xFFFFFFFF; + E = D; + D = C; + C = SHA1CircularShift(30,B); + B = A; + A = temp; + } + + for(t = 20; t < 40; t++) + { + temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[1]; + temp &= 0xFFFFFFFF; + E = D; + D = C; + C = SHA1CircularShift(30,B); + B = A; + A = temp; + } + + for(t = 40; t < 60; t++) + { + temp = SHA1CircularShift(5,A) + + ((B & C) | (B & D) | (C & D)) + E + W[t] + K[2]; + temp &= 0xFFFFFFFF; + E = D; + D = C; + C = SHA1CircularShift(30,B); + B = A; + A = temp; + } + + for(t = 60; t < 80; t++) + { + temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[3]; + temp &= 0xFFFFFFFF; + E = D; + D = C; + C = SHA1CircularShift(30,B); + B = A; + A = temp; + } + + context->Message_Digest[0] = + (context->Message_Digest[0] + A) & 0xFFFFFFFF; + context->Message_Digest[1] = + (context->Message_Digest[1] + B) & 0xFFFFFFFF; + context->Message_Digest[2] = + (context->Message_Digest[2] + C) & 0xFFFFFFFF; + context->Message_Digest[3] = + (context->Message_Digest[3] + D) & 0xFFFFFFFF; + context->Message_Digest[4] = + (context->Message_Digest[4] + E) & 0xFFFFFFFF; + + context->Message_Block_Index = 0; +} + +/* + * SHA1PadMessage + * + * Description: + * According to the standard, the message must be padded to an even + * 512 bits. The first padding bit must be a '1'. The last 64 + * bits represent the length of the original message. All bits in + * between should be 0. This function will pad the message + * according to those rules by filling the Message_Block array + * accordingly. It will also call SHA1ProcessMessageBlock() + * appropriately. When it returns, it can be assumed that the + * message digest has been computed. + * + * Parameters: + * context: [in/out] + * The context to pad + * + * Returns: + * Nothing. + * + * Comments: + * + */ +void SHA1PadMessage(SHA1Context *context) +{ + /* + * Check to see if the current message block is too small to hold + * the initial padding bits and length. If so, we will pad the + * block, process it, and then continue padding into a second + * block. + */ + if (context->Message_Block_Index > 55) + { + context->Message_Block[context->Message_Block_Index++] = 0x80; + while(context->Message_Block_Index < 64) + { + context->Message_Block[context->Message_Block_Index++] = 0; + } + + SHA1ProcessMessageBlock(context); + + while(context->Message_Block_Index < 56) + { + context->Message_Block[context->Message_Block_Index++] = 0; + } + } + else + { + context->Message_Block[context->Message_Block_Index++] = 0x80; + while(context->Message_Block_Index < 56) + { + context->Message_Block[context->Message_Block_Index++] = 0; + } + } + + /* + * Store the message length as the last 8 octets + */ + context->Message_Block[56] = (context->Length_High >> 24) & 0xFF; + context->Message_Block[57] = (context->Length_High >> 16) & 0xFF; + context->Message_Block[58] = (context->Length_High >> 8) & 0xFF; + context->Message_Block[59] = (context->Length_High) & 0xFF; + context->Message_Block[60] = (context->Length_Low >> 24) & 0xFF; + context->Message_Block[61] = (context->Length_Low >> 16) & 0xFF; + context->Message_Block[62] = (context->Length_Low >> 8) & 0xFF; + context->Message_Block[63] = (context->Length_Low) & 0xFF; + + SHA1ProcessMessageBlock(context); +} diff --git a/tools/retrolaunch/sha1.h b/tools/retrolaunch/sha1.h new file mode 100644 index 0000000000..1ca4b104a9 --- /dev/null +++ b/tools/retrolaunch/sha1.h @@ -0,0 +1,54 @@ +/* + * sha1.h + * + * Copyright (C) 1998, 2009 + * Paul E. Jones + * All Rights Reserved + * + ***************************************************************************** + * $Id: sha1.h 12 2009-06-22 19:34:25Z paulej $ + ***************************************************************************** + * + * Description: + * This class implements the Secure Hashing Standard as defined + * in FIPS PUB 180-1 published April 17, 1995. + * + * Many of the variable names in the SHA1Context, especially the + * single character names, were used because those were the names + * used in the publication. + * + * Please read the file sha1.c for more information. + * + */ + +#ifndef _SHA1_H_ +#define _SHA1_H_ + +/* + * This structure will hold context information for the hashing + * operation + */ +typedef struct SHA1Context +{ + unsigned Message_Digest[5]; /* Message Digest (output) */ + + unsigned Length_Low; /* Message length in bits */ + unsigned Length_High; /* Message length in bits */ + + unsigned char Message_Block[64]; /* 512-bit message blocks */ + int Message_Block_Index; /* Index into message block array */ + + int Computed; /* Is the digest computed? */ + int Corrupted; /* Is the message digest corruped? */ +} SHA1Context; + +/* + * Function Prototypes + */ +void SHA1Reset(SHA1Context *); +int SHA1Result(SHA1Context *); +void SHA1Input( SHA1Context *, + const unsigned char *, + unsigned); + +#endif