MiSTer-devel / MegaDrive_MiSTer

Sega Megadrive for MiSTer
44 stars 14 forks source link

SMS cart region detection #17

Open sorgelig opened 9 months ago

sorgelig commented 9 months ago

Not sure if it is possible. Need to investigate it.

birdybro commented 9 months ago

I thought about this for a bit. It's definitely possible, but not all commercially released games followed this guideline:

https://www.smspower.org/Development/ROMHeader#RegionCode0x7fff05Bytes

However, because there is no "PAL" region code, the benefit is almost totally eradicated if you attempt to detect from the region header within the core. Therefore, the only feasible way I can think of would be to use a DB in Main, which is undesirable from a portability standpoint and the principle of keeping complexity of Main to a minimum. A simple python script could be devised to create a DB of the header information and identify all the header characteristics of every SMS game dumped, however there will be a lot of false positives as companies didn't always follow this guideline.

So more of a manual DB using hash checks would have to be created instead, which thankfully the library isn't that large so I could write one up pretty quickly, or maybe a software emulator already has this.

Detecting quirks in Main via a DB lookup is preferable from the perspective of keeping the register count in the core down though, like N64.

EDIT: ekeeke, as usual, has a thorough DB for the various mappers available here in c, so you could understand better the possible benefits:

https://github.com/ekeeke/Genesis-Plus-GX/blob/master/core/cart_hw/sms_cart.c

PAL specific timings --> https://github.com/ekeeke/Genesis-Plus-GX/blob/master/core/cart_hw/sms_cart.c#L223-L242

sorgelig commented 9 months ago

It's not worth to make such DB because there will be always some mods which won't be detected. Core supports manual region change. Even has keyboard hotkeys F1-F3 if you have keyboard nearby (or you can map Button to key on gamepad - now it's permanent).

birdybro commented 9 months ago

Yeah this is kinda how I felt as well. I gave up on adding an auto region switch to the SMS core as a result thanks to your advice at the time then. Then maybe detecting it within the core just for those 19 PAL games in the last link from gpgx I provided would be easiest? It shouldn't use up a ton of registers to do this at such a small scale.

It would default to NTSC video timings except for those 19 games.

birdybro commented 9 months ago

tl;dr probably still not worth it

Using the same method to detect games I used here:

https://github.com/MiSTer-devel/SMS_MiSTer/blob/master/SMS.sv#L554-L560

I came up with this simple code to do this just now if you wanted to try it out (would need to add a && sms_cart check or something like that, also might need a few other adjustments to even work, I haven't familiarized myself with the megadrive core's cart handling code yet):

// Detect the different PAL timing SMS games released in Europe
if(ioctl_wr & cart_download) begin
    if(ioctl_addr == 'h7ffc) cart_id[31:24] <= ioctl_dout[7:0];
    if(ioctl_addr == 'h7ffd) cart_id[23:16] <= ioctl_dout[7:0];
    if(ioctl_addr == 'h7ffe) cart_id[15:08] <= ioctl_dout[7:0];
    if(ioctl_addr == 'h7fff) cart_id[07:00] <= ioctl_dout[7:0];
    if(ioctl_addr == 'h8000) begin
             if(cart_id == 32'h67_70_20_40) sms_pal <= 1; // Addams Family
        else if(cart_id == 32'h20_70_20_40) sms_pal <= 1; // Back to the Future Part III
        else if(cart_id == 32'h58_70_20_40) sms_pal <= 1; // Battlemaniacs (Brazil)
        else if(cart_id == 32'h65_70_2F_40) sms_pal <= 1; // Bram Stoker's Dracula
        else if(cart_id == 32'h05_71_00_40) sms_pal <= 1; // California Games II & Jogos de Varao II (Brazil)
        else if(cart_id == 32'h09_71_00_40) sms_pal <= 1; // Home Alone
        else if(cart_id == 32'h24_90_00_40) sms_pal <= 1; // Power Strike II
        else if(cart_id == 32'h47_70_20_40) sms_pal <= 1; // Predator 2
        else if(cart_id == 32'h19_25_00_40) sms_pal <= 1; // Quest for the Shaven Yak Starring Ren Hoek & Stimpy (Brazil)
        else if(cart_id == 32'h64_70_20_40) sms_pal <= 1; // RoboCop 3
        else if(cart_id == 32'h14_50_20_4f) sms_pal <= 1; // Sensible Soccer
        else if(cart_id == 32'hff_ff_ff_4f) sms_pal <= 1; // Shadow of the Beast (FIRST DUPLICATE FOUND, this is a common unique id used in TONS of games, would be a false positive, almost all of them are Beta's/Samples/unreleased though)
        else if(cart_id == 32'h73_25_00_40) sms_pal <= 1; // Sonic Blast (Brazil)
        else if(cart_id == 32'h15_90_00_40) sms_pal <= 1; // Sonic the Hedgehog 2 (SECOND DUPLICATE FOUND, this is also used for Winter Olympics (Brazil) but no other games from no-intro)
        else if(cart_id == 32'h15_90_01_40) sms_pal <= 1; // Sonic the Hedgehog 2 Rev 1
        else if(cart_id == 32'h80_70_00_40) sms_pal <= 1; // Space Harrier (Europe)
        else if(cart_id == 32'h38_70_00_40) sms_pal <= 1; // Taito Chase H.Q.
        else if(cart_id == 32'h00_00_01_40) sms_pal <= 1; // Taito Chase H.Q. (Beta) & NBA Jam (Prototype)
    end
end

As you can see there are just two exceptions across the No-Intro SMS collection, that Shadow of the Beast's commercial release used the beta/sample/demo game ID that a bunch of other ones used that were never commercially released.

So consider this a feasibility check to demonstrate some of the problems. The amount of hex read could be expanded to find out if there are ways to capture more hex values to make them all truly unique.

The "unique" game id's were collected with the following python code:

import os
import csv

def list_files(startpath):
    """Recursively list all files with the extensions .sms and .gg in the given path."""
    file_list = []
    for root, dirs, files in os.walk(startpath):
        for file in files:
            if file.endswith('.sms') or file.endswith('.gg'):
                full_path = os.path.join(root, file)
                file_list.append(full_path)
    return file_list

def read_bytes_from_file(file_path, start_offset, end_offset):
    """Read and return bytes in hexadecimal from the given file between the specified offsets."""
    with open(file_path, 'rb') as file:
        file.seek(start_offset)
        bytes_data = file.read(end_offset - start_offset)
        return bytes_data.hex()

def save_to_csv(files_data, csv_filename):
    """Save the file data to a CSV."""
    with open(csv_filename, 'w', newline='') as csvfile:
        filewriter = csv.writer(csvfile, delimiter=',', quotechar='"', quoting=csv.QUOTE_MINIMAL)
        filewriter.writerow(['File Path', 'Hex Data'])  # Header
        for file_data in files_data:
            filewriter.writerow([file_data['path'], file_data['hex_data']])

def main():
    """Main function."""
    current_directory = os.getcwd()
    all_files = list_files(current_directory)

    files_data = []
    for file_path in all_files:
        hex_data = read_bytes_from_file(file_path, 0x7ffc, 0x8000)
        files_data.append({
            'path': file_path,
            'hex_data': hex_data
        })

    save_to_csv(files_data, 'hex_data.csv')
    print(f"Hex data written to 'hex_data.csv' in the current directory.")

if __name__ == "__main__":
    main()

Resultant hex data file:

hex_data.csv

sorgelig commented 9 months ago

I don't like this idea. There is no point to make it for 19 games only. MD core is already super heavy. It still has unfixed slacks yet...

You already provided enough info to understand there is no region inside SMS ROM.

birdybro commented 9 months ago

Yup, it's not a great solution. I definitely wasn't suggesting to go with it, it's too many registers and complexity added for just a few games when people in the PAL region generally know they need to switch to Europe manually already. :)

SwedishGojira commented 6 months ago

Would it not be possible to at least make the core switch to PAL for Master System roms if it detects a different extension like say .smp same as the core already do for Genesis/Mega Drive games?

sorgelig commented 6 months ago

it's possible. I don't know if there is a standard for extensions per region

SwedishGojira commented 6 months ago

I don't think there is a "standard" per se, but the genesis and megadrive core set its region according to if you name the extensions to gen/md/bin which is not that standard also, but it works well to force the region for games. I have used it for a long time on MiSTer before it could even set the regions by looking at the header.

sorgelig commented 6 months ago

gen/md extensions at least were exiting and tied to regions. For SMS i don't remember any other extensions. But yes, we can define like smp/smu/smj