|
1
|
|
package com.rox.cartridge; |
|
2
|
|
|
|
3
|
|
import java.util.Arrays; |
|
4
|
|
|
|
5
|
|
/** |
|
6
|
|
* A parsed iNES (".nes") ROM file: the 16-byte header plus the PRG-ROM/CHR-ROM bytes it describes. |
|
7
|
|
* Doesn't know anything about mapper banking behaviour - that's {@link Mapper}'s job, built from the |
|
8
|
|
* data here. |
|
9
|
|
*/ |
|
10
|
|
public final class INesRom { |
|
11
|
|
private static final int HEADER_SIZE = 16; |
|
12
|
|
private static final int TRAINER_SIZE = 512; |
|
13
|
|
private static final int PRG_BANK_SIZE = 16384; |
|
14
|
|
private static final int CHR_BANK_SIZE = 8192; |
|
15
|
|
|
|
16
|
|
private static final int PRG_BANKS_OFFSET = 4; |
|
17
|
|
private static final int CHR_BANKS_OFFSET = 5; |
|
18
|
|
private static final int FLAGS_6_OFFSET = 6; |
|
19
|
|
private static final int FLAGS_7_OFFSET = 7; |
|
20
|
|
|
|
21
|
|
private static final int TRAINER_BIT = 0x04; |
|
22
|
|
private static final int BATTERY_BIT = 0x02; |
|
23
|
|
private static final int VERTICAL_MIRRORING_BIT = 0x01; |
|
24
|
|
private static final int MAPPER_LOW_NIBBLE_SHIFT = 4; |
|
25
|
|
private static final int MAPPER_HIGH_NIBBLE_MASK = 0xF0; |
|
26
|
|
private static final int NES2_IDENTIFICATION_MASK = 0x0C; |
|
27
|
|
private static final int NES2_IDENTIFICATION_VALUE = 0x08; |
|
28
|
|
|
|
29
|
|
private final int mapperNumber; |
|
30
|
|
private final boolean verticalMirroring; |
|
31
|
|
private final boolean hasBattery; |
|
32
|
|
private final boolean hasTrainer; |
|
33
|
|
private final byte[] prgRom; |
|
34
|
|
private final byte[] chrRom; |
|
35
|
|
|
|
36
|
|
private INesRom(final int mapperNumber, final boolean verticalMirroring, final boolean hasBattery, |
|
37
|
|
final boolean hasTrainer, final byte[] prgRom, final byte[] chrRom){ |
|
38
|
|
this.mapperNumber = mapperNumber; |
|
39
|
|
this.verticalMirroring = verticalMirroring; |
|
40
|
|
this.hasBattery = hasBattery; |
|
41
|
|
this.hasTrainer = hasTrainer; |
|
42
|
|
this.prgRom = prgRom; |
|
43
|
|
this.chrRom = chrRom; |
|
44
|
|
} |
|
45
|
|
|
|
46
|
|
/** Parse the raw bytes of a ".nes" file. */ |
|
47
|
|
public static INesRom parse(final byte[] fileBytes){ |
|
48
|
3
1. parse : negated conditional → KILLED
2. parse : changed conditional boundary → KILLED
3. parse : negated conditional → KILLED
|
if (fileBytes.length < HEADER_SIZE || !hasMagic(fileBytes)){ |
|
49
|
|
throw new IllegalArgumentException("Not an iNES ROM (missing 'NES' + $1A header magic)"); |
|
50
|
|
} |
|
51
|
|
|
|
52
|
1
1. parse : Replaced bitwise AND with OR → KILLED
|
final int flags7 = fileBytes[FLAGS_7_OFFSET] & 0xFF; |
|
53
|
2
1. parse : Replaced bitwise AND with OR → KILLED
2. parse : negated conditional → KILLED
|
if ((flags7 & NES2_IDENTIFICATION_MASK) == NES2_IDENTIFICATION_VALUE){ |
|
54
|
|
throw new IllegalArgumentException("NES 2.0 ROMs are not supported yet - flags7 identifies this as NES 2.0"); |
|
55
|
|
} |
|
56
|
|
|
|
57
|
1
1. parse : Replaced bitwise AND with OR → KILLED
|
final int prgBanks = fileBytes[PRG_BANKS_OFFSET] & 0xFF; |
|
58
|
1
1. parse : Replaced bitwise AND with OR → KILLED
|
final int chrBanks = fileBytes[CHR_BANKS_OFFSET] & 0xFF; |
|
59
|
1
1. parse : Replaced bitwise AND with OR → KILLED
|
final int flags6 = fileBytes[FLAGS_6_OFFSET] & 0xFF; |
|
60
|
|
|
|
61
|
3
1. parse : Replaced Shift Right with Shift Left → KILLED
2. parse : Replaced bitwise OR with AND → KILLED
3. parse : Replaced bitwise AND with OR → KILLED
|
final int mapperNumber = (flags7 & MAPPER_HIGH_NIBBLE_MASK) | (flags6 >> MAPPER_LOW_NIBBLE_SHIFT); |
|
62
|
2
1. parse : negated conditional → KILLED
2. parse : Replaced bitwise AND with OR → KILLED
|
final boolean verticalMirroring = (flags6 & VERTICAL_MIRRORING_BIT) != 0; |
|
63
|
2
1. parse : negated conditional → KILLED
2. parse : Replaced bitwise AND with OR → KILLED
|
final boolean hasBattery = (flags6 & BATTERY_BIT) != 0; |
|
64
|
2
1. parse : Replaced bitwise AND with OR → KILLED
2. parse : negated conditional → KILLED
|
final boolean hasTrainer = (flags6 & TRAINER_BIT) != 0; |
|
65
|
|
|
|
66
|
2
1. parse : negated conditional → KILLED
2. parse : Replaced integer addition with subtraction → KILLED
|
int offset = HEADER_SIZE + (hasTrainer ? TRAINER_SIZE : 0); |
|
67
|
|
|
|
68
|
1
1. parse : Replaced integer multiplication with division → KILLED
|
final int prgSize = prgBanks * PRG_BANK_SIZE; |
|
69
|
|
final byte[] prgRom = copyRange(fileBytes, offset, prgSize, "PRG-ROM"); |
|
70
|
1
1. parse : Replaced integer addition with subtraction → KILLED
|
offset += prgSize; |
|
71
|
|
|
|
72
|
1
1. parse : Replaced integer multiplication with division → KILLED
|
final int chrSize = chrBanks * CHR_BANK_SIZE; |
|
73
|
|
final byte[] chrRom = copyRange(fileBytes, offset, chrSize, "CHR-ROM"); |
|
74
|
|
|
|
75
|
1
1. parse : replaced return value with null for com/rox/cartridge/INesRom::parse → KILLED
|
return new INesRom(mapperNumber, verticalMirroring, hasBattery, hasTrainer, prgRom, chrRom); |
|
76
|
|
} |
|
77
|
|
|
|
78
|
|
private static boolean hasMagic(final byte[] fileBytes){ |
|
79
|
5
1. hasMagic : replaced boolean return with true for com/rox/cartridge/INesRom::hasMagic → KILLED
2. hasMagic : negated conditional → KILLED
3. hasMagic : negated conditional → KILLED
4. hasMagic : negated conditional → KILLED
5. hasMagic : negated conditional → KILLED
|
return fileBytes[0] == 'N' && fileBytes[1] == 'E' && fileBytes[2] == 'S' && fileBytes[3] == 0x1A; |
|
80
|
|
} |
|
81
|
|
|
|
82
|
|
private static byte[] copyRange(final byte[] fileBytes, final int offset, final int length, final String label){ |
|
83
|
3
1. copyRange : changed conditional boundary → KILLED
2. copyRange : negated conditional → KILLED
3. copyRange : Replaced integer addition with subtraction → KILLED
|
if (offset + length > fileBytes.length){ |
|
84
|
|
throw new IllegalArgumentException("Truncated iNES ROM: expected " + length + " bytes of " + label |
|
85
|
|
+ " at offset " + offset + ", file is only " + fileBytes.length + " bytes"); |
|
86
|
|
} |
|
87
|
2
1. copyRange : replaced return value with null for com/rox/cartridge/INesRom::copyRange → KILLED
2. copyRange : Replaced integer addition with subtraction → KILLED
|
return Arrays.copyOfRange(fileBytes, offset, offset + length); |
|
88
|
|
} |
|
89
|
|
|
|
90
|
|
public int mapperNumber(){ |
|
91
|
1
1. mapperNumber : replaced int return with 0 for com/rox/cartridge/INesRom::mapperNumber → KILLED
|
return mapperNumber; |
|
92
|
|
} |
|
93
|
|
|
|
94
|
|
public boolean isVerticalMirroring(){ |
|
95
|
2
1. isVerticalMirroring : replaced boolean return with true for com/rox/cartridge/INesRom::isVerticalMirroring → KILLED
2. isVerticalMirroring : replaced boolean return with false for com/rox/cartridge/INesRom::isVerticalMirroring → KILLED
|
return verticalMirroring; |
|
96
|
|
} |
|
97
|
|
|
|
98
|
|
/** Whether this cartridge's PRG-RAM ({@code $6000-$7FFF}) is battery-backed - flags6 bit 1 of the iNES header. */ |
|
99
|
|
public boolean hasBattery(){ |
|
100
|
2
1. hasBattery : replaced boolean return with true for com/rox/cartridge/INesRom::hasBattery → KILLED
2. hasBattery : replaced boolean return with false for com/rox/cartridge/INesRom::hasBattery → KILLED
|
return hasBattery; |
|
101
|
|
} |
|
102
|
|
|
|
103
|
|
/** Whether the file includes a 512-byte trainer before PRG-ROM - flags6 bit 2 of the iNES header. */ |
|
104
|
|
public boolean hasTrainer(){ |
|
105
|
2
1. hasTrainer : replaced boolean return with true for com/rox/cartridge/INesRom::hasTrainer → KILLED
2. hasTrainer : replaced boolean return with false for com/rox/cartridge/INesRom::hasTrainer → KILLED
|
return hasTrainer; |
|
106
|
|
} |
|
107
|
|
|
|
108
|
|
/** Defensive copy - callers must not be able to mutate ROM content behind PRG-ROM's back. */ |
|
109
|
|
public byte[] prgRom(){ |
|
110
|
1
1. prgRom : replaced return value with null for com/rox/cartridge/INesRom::prgRom → KILLED
|
return Arrays.copyOf(prgRom, prgRom.length); |
|
111
|
|
} |
|
112
|
|
|
|
113
|
|
/** Defensive copy - callers must not be able to mutate ROM content behind CHR-ROM's back. */ |
|
114
|
|
public byte[] chrRom(){ |
|
115
|
1
1. chrRom : replaced return value with null for com/rox/cartridge/INesRom::chrRom → KILLED
|
return Arrays.copyOf(chrRom, chrRom.length); |
|
116
|
|
} |
|
117
|
|
} |
| | Mutations |
| 48 |
|
1.1 Location : parse Killed by : com.rox.cartridge.INesRomTest.[engine:junit-jupiter]/[class:com.rox.cartridge.INesRomTest]/[test-template:rejectsFileWithAnySingleMagicByteWrong(int)]/[test-template-invocation:#1] negated conditional → KILLED
2.2 Location : parse Killed by : com.rox.cartridge.INesRomTest.[engine:junit-jupiter]/[class:com.rox.cartridge.INesRomTest]/[method:acceptsFileExactlyTheHeaderSizeWithNoPrgOrChrBanks()] changed conditional boundary → KILLED
3.3 Location : parse Killed by : com.rox.cartridge.INesRomTest.[engine:junit-jupiter]/[class:com.rox.cartridge.INesRomTest]/[method:parsesMapperNumberFromLowNibbleOfFlags6()] negated conditional → KILLED
|
| 52 |
|
1.1 Location : parse Killed by : com.rox.cartridge.INesRomTest.[engine:junit-jupiter]/[class:com.rox.cartridge.INesRomTest]/[method:rejectsNes2FormatHeaders()] Replaced bitwise AND with OR → KILLED
|
| 53 |
|
1.1 Location : parse Killed by : com.rox.cartridge.INesRomTest.[engine:junit-jupiter]/[class:com.rox.cartridge.INesRomTest]/[method:rejectsNes2FormatHeaders()] Replaced bitwise AND with OR → KILLED
2.2 Location : parse Killed by : com.rox.cartridge.INesRomTest.[engine:junit-jupiter]/[class:com.rox.cartridge.INesRomTest]/[method:rejectsNes2FormatHeaders()] negated conditional → KILLED
|
| 57 |
|
1.1 Location : parse Killed by : com.rox.cartridge.INesRomTest.[engine:junit-jupiter]/[class:com.rox.cartridge.INesRomTest]/[method:parsesMapperNumberFromLowNibbleOfFlags6()] Replaced bitwise AND with OR → KILLED
|
| 58 |
|
1.1 Location : parse Killed by : com.rox.cartridge.INesRomTest.[engine:junit-jupiter]/[class:com.rox.cartridge.INesRomTest]/[method:parsesMapperNumberFromLowNibbleOfFlags6()] Replaced bitwise AND with OR → KILLED
|
| 59 |
|
1.1 Location : parse Killed by : com.rox.cartridge.INesRomTest.[engine:junit-jupiter]/[class:com.rox.cartridge.INesRomTest]/[method:parsesMapperNumberFromLowNibbleOfFlags6()] Replaced bitwise AND with OR → KILLED
|
| 61 |
|
1.1 Location : parse Killed by : com.rox.cartridge.INesRomTest.[engine:junit-jupiter]/[class:com.rox.cartridge.INesRomTest]/[method:parsesMapperNumberFromLowNibbleOfFlags6()] Replaced Shift Right with Shift Left → KILLED
2.2 Location : parse Killed by : com.rox.cartridge.INesRomTest.[engine:junit-jupiter]/[class:com.rox.cartridge.INesRomTest]/[method:parsesMapperNumberFromLowNibbleOfFlags6()] Replaced bitwise OR with AND → KILLED
3.3 Location : parse Killed by : com.rox.cartridge.INesRomTest.[engine:junit-jupiter]/[class:com.rox.cartridge.INesRomTest]/[method:parsesMapperNumberFromLowNibbleOfFlags6()] Replaced bitwise AND with OR → KILLED
|
| 62 |
|
1.1 Location : parse Killed by : com.rox.cartridge.INesRomTest.[engine:junit-jupiter]/[class:com.rox.cartridge.INesRomTest]/[method:verticalMirroringBitIsRead()] negated conditional → KILLED
2.2 Location : parse Killed by : com.rox.cartridge.INesRomTest.[engine:junit-jupiter]/[class:com.rox.cartridge.INesRomTest]/[method:verticalMirroringBitIsRead()] Replaced bitwise AND with OR → KILLED
|
| 63 |
|
1.1 Location : parse Killed by : com.rox.cartridge.INesRomTest.[engine:junit-jupiter]/[class:com.rox.cartridge.INesRomTest]/[method:batteryBitIsRead()] negated conditional → KILLED
2.2 Location : parse Killed by : com.rox.cartridge.INesRomTest.[engine:junit-jupiter]/[class:com.rox.cartridge.INesRomTest]/[method:batteryBitIsRead()] Replaced bitwise AND with OR → KILLED
|
| 64 |
|
1.1 Location : parse Killed by : com.rox.cartridge.INesRomTest.[engine:junit-jupiter]/[class:com.rox.cartridge.INesRomTest]/[method:parsesMapperNumberFromLowNibbleOfFlags6()] Replaced bitwise AND with OR → KILLED
2.2 Location : parse Killed by : com.rox.cartridge.INesRomTest.[engine:junit-jupiter]/[class:com.rox.cartridge.INesRomTest]/[method:parsesMapperNumberFromLowNibbleOfFlags6()] negated conditional → KILLED
|
| 66 |
|
1.1 Location : parse Killed by : com.rox.cartridge.INesRomTest.[engine:junit-jupiter]/[class:com.rox.cartridge.INesRomTest]/[method:parsesMapperNumberFromLowNibbleOfFlags6()] negated conditional → KILLED
2.2 Location : parse Killed by : com.rox.cartridge.INesRomTest.[engine:junit-jupiter]/[class:com.rox.cartridge.INesRomTest]/[method:trainerBitIsRead()] Replaced integer addition with subtraction → KILLED
|
| 68 |
|
1.1 Location : parse Killed by : com.rox.cartridge.INesRomTest.[engine:junit-jupiter]/[class:com.rox.cartridge.INesRomTest]/[method:rejectsPrgTruncationEvenWhenChrIsAlsoDeclared()] Replaced integer multiplication with division → KILLED
|
| 70 |
|
1.1 Location : parse Killed by : com.rox.cartridge.INesRomTest.[engine:junit-jupiter]/[class:com.rox.cartridge.INesRomTest]/[method:parsesMapperNumberFromLowNibbleOfFlags6()] Replaced integer addition with subtraction → KILLED
|
| 72 |
|
1.1 Location : parse Killed by : com.rox.cartridge.INesRomTest.[engine:junit-jupiter]/[class:com.rox.cartridge.INesRomTest]/[method:chrRomGetterReturnsADefensiveCopy()] Replaced integer multiplication with division → KILLED
|
| 75 |
|
1.1 Location : parse Killed by : com.rox.cartridge.INesRomTest.[engine:junit-jupiter]/[class:com.rox.cartridge.INesRomTest]/[method:parsesMapperNumberFromLowNibbleOfFlags6()] replaced return value with null for com/rox/cartridge/INesRom::parse → KILLED
|
| 79 |
|
1.1 Location : hasMagic Killed by : com.rox.cartridge.INesRomTest.[engine:junit-jupiter]/[class:com.rox.cartridge.INesRomTest]/[test-template:rejectsFileWithAnySingleMagicByteWrong(int)]/[test-template-invocation:#1] replaced boolean return with true for com/rox/cartridge/INesRom::hasMagic → KILLED
2.2 Location : hasMagic Killed by : com.rox.cartridge.INesRomTest.[engine:junit-jupiter]/[class:com.rox.cartridge.INesRomTest]/[test-template:rejectsFileWithAnySingleMagicByteWrong(int)]/[test-template-invocation:#4] negated conditional → KILLED
3.3 Location : hasMagic Killed by : com.rox.cartridge.INesRomTest.[engine:junit-jupiter]/[class:com.rox.cartridge.INesRomTest]/[test-template:rejectsFileWithAnySingleMagicByteWrong(int)]/[test-template-invocation:#2] negated conditional → KILLED
4.4 Location : hasMagic Killed by : com.rox.cartridge.INesRomTest.[engine:junit-jupiter]/[class:com.rox.cartridge.INesRomTest]/[test-template:rejectsFileWithAnySingleMagicByteWrong(int)]/[test-template-invocation:#3] negated conditional → KILLED
5.5 Location : hasMagic Killed by : com.rox.cartridge.INesRomTest.[engine:junit-jupiter]/[class:com.rox.cartridge.INesRomTest]/[test-template:rejectsFileWithAnySingleMagicByteWrong(int)]/[test-template-invocation:#1] negated conditional → KILLED
|
| 83 |
|
1.1 Location : copyRange Killed by : com.rox.cartridge.INesRomTest.[engine:junit-jupiter]/[class:com.rox.cartridge.INesRomTest]/[method:parsesMapperNumberFromLowNibbleOfFlags6()] changed conditional boundary → KILLED
2.2 Location : copyRange Killed by : com.rox.cartridge.INesRomTest.[engine:junit-jupiter]/[class:com.rox.cartridge.INesRomTest]/[method:rejectsPrgTruncationEvenWhenChrIsAlsoDeclared()] negated conditional → KILLED
3.3 Location : copyRange Killed by : com.rox.cartridge.INesRomTest.[engine:junit-jupiter]/[class:com.rox.cartridge.INesRomTest]/[method:rejectsPrgTruncationEvenWhenChrIsAlsoDeclared()] Replaced integer addition with subtraction → KILLED
|
| 87 |
|
1.1 Location : copyRange Killed by : com.rox.cartridge.INesRomTest.[engine:junit-jupiter]/[class:com.rox.cartridge.INesRomTest]/[method:acceptsFileExactlyTheHeaderSizeWithNoPrgOrChrBanks()] replaced return value with null for com/rox/cartridge/INesRom::copyRange → KILLED
2.2 Location : copyRange Killed by : com.rox.cartridge.INesRomTest.[engine:junit-jupiter]/[class:com.rox.cartridge.INesRomTest]/[method:parsesMapperNumberFromLowNibbleOfFlags6()] Replaced integer addition with subtraction → KILLED
|
| 91 |
|
1.1 Location : mapperNumber Killed by : com.rox.cartridge.INesRomTest.[engine:junit-jupiter]/[class:com.rox.cartridge.INesRomTest]/[method:parsesMapperNumberFromLowNibbleOfFlags6()] replaced int return with 0 for com/rox/cartridge/INesRom::mapperNumber → KILLED
|
| 95 |
|
1.1 Location : isVerticalMirroring Killed by : com.rox.cartridge.INesRomTest.[engine:junit-jupiter]/[class:com.rox.cartridge.INesRomTest]/[method:verticalMirroringBitIsRead()] replaced boolean return with true for com/rox/cartridge/INesRom::isVerticalMirroring → KILLED
2.2 Location : isVerticalMirroring Killed by : com.rox.cartridge.INesRomTest.[engine:junit-jupiter]/[class:com.rox.cartridge.INesRomTest]/[method:verticalMirroringBitIsRead()] replaced boolean return with false for com/rox/cartridge/INesRom::isVerticalMirroring → KILLED
|
| 100 |
|
1.1 Location : hasBattery Killed by : com.rox.cartridge.INesRomTest.[engine:junit-jupiter]/[class:com.rox.cartridge.INesRomTest]/[method:batteryBitIsRead()] replaced boolean return with true for com/rox/cartridge/INesRom::hasBattery → KILLED
2.2 Location : hasBattery Killed by : com.rox.cartridge.INesRomTest.[engine:junit-jupiter]/[class:com.rox.cartridge.INesRomTest]/[method:batteryBitIsRead()] replaced boolean return with false for com/rox/cartridge/INesRom::hasBattery → KILLED
|
| 105 |
|
1.1 Location : hasTrainer Killed by : com.rox.cartridge.INesRomTest.[engine:junit-jupiter]/[class:com.rox.cartridge.INesRomTest]/[method:trainerBitIsRead()] replaced boolean return with true for com/rox/cartridge/INesRom::hasTrainer → KILLED
2.2 Location : hasTrainer Killed by : com.rox.cartridge.INesRomTest.[engine:junit-jupiter]/[class:com.rox.cartridge.INesRomTest]/[method:trainerBitIsRead()] replaced boolean return with false for com/rox/cartridge/INesRom::hasTrainer → KILLED
|
| 110 |
|
1.1 Location : prgRom Killed by : com.rox.cartridge.INesRomTest.[engine:junit-jupiter]/[class:com.rox.cartridge.INesRomTest]/[method:acceptsFileExactlyTheHeaderSizeWithNoPrgOrChrBanks()] replaced return value with null for com/rox/cartridge/INesRom::prgRom → KILLED
|
| 115 |
|
1.1 Location : chrRom Killed by : com.rox.cartridge.INesRomTest.[engine:junit-jupiter]/[class:com.rox.cartridge.INesRomTest]/[method:chrRomGetterReturnsADefensiveCopy()] replaced return value with null for com/rox/cartridge/INesRom::chrRom → KILLED
|