Skip to content

Commit

Permalink
helios: refactor region handling
Browse files Browse the repository at this point in the history
  • Loading branch information
Federico Berti committed Aug 10, 2024
1 parent a63c4d7 commit d7e9cc3
Show file tree
Hide file tree
Showing 13 changed files with 224 additions and 423 deletions.
14 changes: 6 additions & 8 deletions src/main/java/mcd/MegaCd.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
package mcd;

import mcd.bus.McdSubInterruptHandler;
import mcd.cart.MegaCdCartInfoProvider;
import mcd.cdd.ExtendedCueSheet;
import mcd.util.McdMemView;
import omegadrive.SystemLoader;
Expand All @@ -36,7 +37,6 @@
import omegadrive.memory.MemoryProvider;
import omegadrive.savestate.BaseStateHandler;
import omegadrive.system.BaseSystem;
import omegadrive.system.Megadrive;
import omegadrive.system.SysUtil;
import omegadrive.system.SystemProvider;
import omegadrive.ui.DisplayWindow;
Expand Down Expand Up @@ -239,16 +239,14 @@ private double getMicrosPerTick() {
return 1_000_000.0 / (mclkhz / (FM_DIVIDER * MCLK_DIVIDER));
}

@Override
protected RegionDetector.Region getRomRegion() {
return RegionDetector.detectRegion((MdCartInfoProvider) romContext.cartridgeInfoProvider);
}

@Override
protected RomContext createRomContext(SysUtil.RomSpec rom) {
romContext = Megadrive.createRomContext(rom, memory, emuFrame.getRegionOverride());
assert !ZipUtil.isCompressedByteStream(rom.file);
return romContext;
RomContext rc = new RomContext(rom);
MdCartInfoProvider mcip = MegaCdCartInfoProvider.createMcdInstance(memory, rc);
rc.cartridgeInfoProvider = mcip;
rc.region = RegionDetector.selectRegion(display, mcip);
return rc;
}

@Override
Expand Down
129 changes: 129 additions & 0 deletions src/main/java/mcd/cart/MegaCdCartInfoProvider.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package mcd.cart;

import mcd.cdd.CdModel;
import omegadrive.cart.MdCartInfoProvider;
import omegadrive.memory.IMemoryProvider;
import omegadrive.system.SysUtil;
import omegadrive.system.SystemProvider;
import omegadrive.util.LogHelper;
import omegadrive.util.RegionDetector;
import omegadrive.util.Util;
import omegadrive.vdp.util.MemView;
import org.slf4j.Logger;

import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.util.Arrays;

/**
* Federico Berti
* <p>
* Copyright 2024
*/
public class MegaCdCartInfoProvider extends MdCartInfoProvider {

private final static Logger LOG = LogHelper.getLogger(MegaCdCartInfoProvider.class.getSimpleName());

private final static int headerLen = 0x10;

private final static int TRACK01_SECURITY_CODE_START = 0x200;
private final static byte[] SCD_SYS_BYTES = "SEGADISCSYSTEM".getBytes();
private final static byte ff = (byte) 0xff;
private final static byte[] CD_SYNC_BYTES = {0x00, ff, ff, ff, ff, ff, ff, ff, ff, ff, ff, 0x00};

enum SecurityCodeInfo {
USA(RegionDetector.Region.USA, "d7b7e8e00cee68e15828197ee5090f5a14acd436", 0x584),
EU(RegionDetector.Region.EUROPE, "4a1946f9aaf7261d9d4ccb0556b7342eaf1cf2f8", 0x56E),
JP(RegionDetector.Region.JAPAN, "ffe78f1ffdb8b76b358eb8a29b1d5f9dbeabacca", 0x156);
public final RegionDetector.Region region;
public final String sha1;
public final int length;

SecurityCodeInfo(RegionDetector.Region region, String sha1, int length) {
this.region = region;
this.sha1 = sha1;
this.length = length;
}
}


public SysUtil.RomFileType detectedRomFileType;
public RegionDetector.Region securityCodeRegion;


public static MegaCdCartInfoProvider createMcdInstance(IMemoryProvider memoryProvider, SystemProvider.RomContext rom) {
MegaCdCartInfoProvider m = new MegaCdCartInfoProvider(memoryProvider, rom);
m.init();
return m;
}

private MegaCdCartInfoProvider(IMemoryProvider memoryProvider, SystemProvider.RomContext rom) {
super(memoryProvider, rom);
}

@Override
protected void init() {
super.init();
CdModel.ExtendedTrackData t1 = romContext.sheet.extTracks.get(0);
checkTrack01Header(t1);
securityCodeRegion = verifySecurityCodeRegion(t1);
}

private void checkTrack01Header(CdModel.ExtendedTrackData track01) {
//check that *.iso is really an iso file internally
SysUtil.RomFileType romFileType = romContext.sheet.romFileType;
try {
byte[] header = new byte[headerLen];
RandomAccessFile raf = track01.file;
raf.seek(0);
CdModel.TrackDataType trackDataType = track01.trackDataType;
raf.read(header, 0, header.length);
detectedRomFileType = SysUtil.RomFileType.UNKNOWN;
if (Arrays.equals(SCD_SYS_BYTES, 0, SCD_SYS_BYTES.length, header, 0, SCD_SYS_BYTES.length)) {
System.out.println("valid Sega CD image");
detectedRomFileType = SysUtil.RomFileType.ISO;
} else if (Arrays.equals(CD_SYNC_BYTES, 0, CD_SYNC_BYTES.length, header, 0, CD_SYNC_BYTES.length)) {
System.out.println("CD-ROM synchro pattern");
detectedRomFileType = SysUtil.RomFileType.BIN_CUE;
} else if (trackDataType == CdModel.TrackDataType.AUDIO) {
System.out.println("CD-AUDIO");
detectedRomFileType = SysUtil.RomFileType.BIN_CUE;
}
assert detectedRomFileType == romFileType : detectedRomFileType + " vs " + romFileType;
} catch (Exception e) {
e.printStackTrace();
LOG.error(e.getMessage());
}
}

private static RegionDetector.Region verifySecurityCodeRegion(CdModel.ExtendedTrackData track01) {
try {
byte[] secCode = new byte[0x700];
//sector 2352 starts with 0x10 sync/header bytes, ignore them
int secCodeStart = track01.trackDataType.size == CdModel.SectorSize.S_2048 ?
TRACK01_SECURITY_CODE_START : TRACK01_SECURITY_CODE_START + 0x10;
RandomAccessFile raf = track01.file;
raf.seek(secCodeStart);
raf.read(secCode);
ByteBuffer bb = ByteBuffer.wrap(secCode);
for (SecurityCodeInfo sci : SecurityCodeInfo.values()) {
byte[] b = new byte[sci.length];
bb.position(0);
bb.get(b);
String sha1 = Util.computeSha1Sum(b);
if (sci.sha1.equalsIgnoreCase(sha1)) {
System.out.println(sci.region);
return sci.region;
}
}
LOG.error("Unknown security code!");
StringBuilder sb = new StringBuilder(track01 + "\n");
MemView.fillFormattedString(sb, secCode, 0, secCode.length);
System.out.println(sb);
} catch (Exception e) {
e.printStackTrace();
LOG.error(e.getMessage());
}
return null;
}
}
65 changes: 6 additions & 59 deletions src/main/java/mcd/cdd/ExtendedCueSheet.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@
import org.digitalmediaserver.cuelib.TrackData;
import org.slf4j.Logger;

import java.io.*;
import java.io.ByteArrayInputStream;
import java.io.Closeable;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
Expand All @@ -28,13 +31,6 @@ public class ExtendedCueSheet implements Closeable {

private final static Logger LOG = LogHelper.getLogger(ExtendedCueSheet.class.getSimpleName());

private final static byte[] SCD_SYS_BYTES = "SEGADISCSYSTEM".getBytes();
private final static byte ff = (byte) 0xff;
private final static byte[] CD_SYNC_BYTES = {0x00, ff, ff, ff, ff, ff, ff, ff, ff, ff, ff, 0x00};

//TODO remove at some point
private static final boolean CUE_TEST_MODE = false;
private static final Path NO_BIN_FILE_PATH = Paths.get(".", "test.bin");
//placeholder name
private static final String TEMPLATE_ISO_NAME_PH = "<iso_here>";

Expand All @@ -52,13 +48,6 @@ public class ExtendedCueSheet implements Closeable {
public final List<ExtendedTrackData> extTracks = new ArrayList<>();
public int numTracks, sectorEnd;
protected final Map<String, RandomAccessFile> fileCache = new HashMap<>();
private static RandomAccessFile NO_BIN_FILE;

static {
if (CUE_TEST_MODE) {
LOG.warn("Cue test mode: {}", CUE_TEST_MODE);
}
}

public ExtendedCueSheet(Path discImage, RomFileType rft) {
assert rft.isDiscImage();
Expand Down Expand Up @@ -107,35 +96,11 @@ private void parseCueSheet() {
List<TrackData> tracks = extCueSheet.cueSheet.getAllTrackData();
assert !tracks.isEmpty();
extCueSheet.numTracks = tracks.size();
checkTrack01Header(tracks.get(0));
for (TrackData track : tracks) {
parseTrack(extCueSheet, track.getNumber(), cuePath);
}
}

private void checkTrack01Header(TrackData track01) {
try {
byte[] header = new byte[16];
RandomAccessFile raf = getDataFile(this, track01.getParent().getFile(), cuePath);
TrackDataType trackDataType = TrackDataType.parse(track01.getDataType());
raf.read(header, 0, header.length);
RomFileType detected = RomFileType.UNKNOWN;
if (Arrays.equals(SCD_SYS_BYTES, 0, SCD_SYS_BYTES.length, header, 0, SCD_SYS_BYTES.length)) {
System.out.println("valid Sega CD image");
detected = RomFileType.ISO;
} else if (Arrays.equals(CD_SYNC_BYTES, 0, CD_SYNC_BYTES.length, header, 0, CD_SYNC_BYTES.length)) {
System.out.println("CD-ROM synchro pattern");
detected = RomFileType.BIN_CUE;
} else if (trackDataType == TrackDataType.AUDIO) {
System.out.println("CD-AUDIO");
detected = RomFileType.BIN_CUE;
}
assert detected == romFileType : detected + " vs " + romFileType;
} catch (Exception e) {
e.printStackTrace();
}
}

private void parseTrack(ExtendedCueSheet extCueSheet, int trackNumber, Path cuePath) {
TrackData trackData = getTrack(extCueSheet.cueSheet, trackNumber);
RandomAccessFile raf = getDataFile(extCueSheet, trackData.getParent().getFile(), cuePath);
Expand Down Expand Up @@ -171,15 +136,12 @@ private void parseTrack(ExtendedCueSheet extCueSheet, int trackNumber, Path cueP
}

private static RandomAccessFile getDataFile(ExtendedCueSheet extCueSheet, String key, Path file, Path cuePath) {
//TODO close RandomAccessFile
if (!extCueSheet.fileCache.containsKey(key)) {
try {
extCueSheet.fileCache.put(key, new RandomAccessFile(file.toFile(), "r"));
} catch (Exception e) {
if (CUE_TEST_MODE) {
handleTestCueMode(extCueSheet, cuePath, key);
} else {
e.printStackTrace();
}
e.printStackTrace();
}
}
return extCueSheet.fileCache.get(key);
Expand All @@ -191,21 +153,6 @@ private static RandomAccessFile getDataFile(ExtendedCueSheet extCueSheet, String
return getDataFile(extCueSheet, key, fp, cuePath);
}

private static void handleTestCueMode(ExtendedCueSheet extCueSheet, Path cuePath, String key) {
try {
if (NO_BIN_FILE == null) {
Path p = cuePath.resolveSibling(NO_BIN_FILE_PATH);
NO_BIN_FILE = new RandomAccessFile(p.toFile(), "r");
LOG.warn("Missing bin file: {}, using instead: {}, cue_test_mode: {}", key, p.toAbsolutePath(), CUE_TEST_MODE);
}
extCueSheet.fileCache.put(key, NO_BIN_FILE);
extCueSheet.romFileType = RomFileType.BIN_CUE;
} catch (FileNotFoundException ex) {
LOG.error("Missing bin file: {}", key);
throw new RuntimeException(ex);
}
}

private static TrackData getTrack(CueSheet cueSheet, int number) {
assert number > 0;
TrackData td = cueSheet.getAllTrackData().get(number - 1);
Expand Down
16 changes: 11 additions & 5 deletions src/main/java/omegadrive/cart/MdCartInfoProvider.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import omegadrive.cart.loader.MdRomDbModel;
import omegadrive.cart.mapper.md.MdMapperType;
import omegadrive.memory.IMemoryProvider;
import omegadrive.memory.MemoryProvider;
import omegadrive.system.SysUtil;
import omegadrive.util.LogHelper;
import omegadrive.util.Size;
Expand Down Expand Up @@ -190,12 +191,17 @@ public String toString() {
return sb.append(headerInfo).toString();
}

public static final MdCartInfoProvider NO_PROVIDER = new MdCartInfoProvider(MemoryProvider.NO_MEMORY, RomContext.NO_ROM);

public static MdCartInfoProvider createMdInstance(IMemoryProvider memoryProvider, RomContext rom) {
MdCartInfoProvider provider = new MdCartInfoProvider();
provider.memoryProvider = memoryProvider;
provider.romContext = rom;
provider.init();
return provider;
MdCartInfoProvider m = new MdCartInfoProvider(memoryProvider, rom);
m.init();
return m;
}

protected MdCartInfoProvider(IMemoryProvider memoryProvider, RomContext rom) {
this.memoryProvider = memoryProvider;
this.romContext = rom;
}

public boolean isSramUsedWithBrokenHeader(long address) {
Expand Down
18 changes: 0 additions & 18 deletions src/main/java/omegadrive/sound/msumd/CueFileParser.java
Original file line number Diff line number Diff line change
@@ -1,21 +1,3 @@
/*
* Copyright (C) 2003, 2014 Graham Sanderson
*
* This file is part of JPSX.
*
* JPSX is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* JPSX is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with JPSX. If not, see <http://www.gnu.org/licenses/>.
*/
package omegadrive.sound.msumd;

import omegadrive.util.LogHelper;
Expand Down
Loading

0 comments on commit d7e9cc3

Please sign in to comment.