Skip to content

Commit

Permalink
[major] Merge pull request #8 from cfogrady/BEMReading
Browse files Browse the repository at this point in the history
[major] BEM Reading and Writing
  • Loading branch information
cfogrady authored Feb 22, 2023
2 parents 4c7dcc1 + bcf24bd commit da32c66
Show file tree
Hide file tree
Showing 100 changed files with 2,800 additions and 1,021 deletions.
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,13 @@ build
bin/

test.bin

modified.bin

new_background.bmp

original.bin

BEM_CARD_IMAGE.bin

MODIFIED_BEM.bin
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.github.cfogrady.vb.dim.adventure;

import lombok.Data;
import lombok.experimental.SuperBuilder;

import java.util.List;

@Data
@SuperBuilder(toBuilder = true)
public class AdventureLevels<T extends AdventureLevels.AdventureLevel> {

@Data
@SuperBuilder(toBuilder = true)
public static class AdventureLevel {
private final int steps;
private final int bossCharacterIndex;
private final int bossDp;
private final int bossHp;
private final int bossAp;
}

private final List<T> levels;
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package com.github.cfogrady.vb.dim.reader.writer;
package com.github.cfogrady.vb.dim.adventure;

import com.github.cfogrady.vb.dim.reader.ByteUtils;
import com.github.cfogrady.vb.dim.reader.content.DimAdventures;
import com.github.cfogrady.vb.dim.card.DimWriter;
import com.github.cfogrady.vb.dim.util.ByteUtils;
import com.github.cfogrady.vb.dim.util.OutputStreamWithNot;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
Expand All @@ -10,13 +11,13 @@
public class AdventuresWriter {
public static void writeAdventures(DimAdventures dimAdventures, OutputStreamWithNot outputStreamWithNot) throws IOException {
outputStreamWithNot.writeZerosUntilOffset(0x50000);
if(dimAdventures.getAdventureBlocks().size() != DimAdventures.VB_TABLE_SIZE) {
if(dimAdventures.getLevels().size() != DimAdventures.VB_TABLE_SIZE) {
log.warn("Unexpected number of adventure missions... Might not work.");
}
int currentIndex = 0;
for(DimAdventures.DimAdventureBlock adventureEntry : dimAdventures.getAdventureBlocks()) {
for(DimAdventures.AdventureLevel adventureEntry : dimAdventures.getLevels()) {
outputStreamWithNot.writeBytes(ByteUtils.convert16BitIntToBytes(adventureEntry.getSteps()));
outputStreamWithNot.writeBytes(ByteUtils.convert16BitIntToBytes(adventureEntry.getBossStatsIndex()));
outputStreamWithNot.writeBytes(ByteUtils.convert16BitIntToBytes(adventureEntry.getBossCharacterIndex()));
outputStreamWithNot.writeBytes(ByteUtils.convert16BitIntToBytes(adventureEntry.getBossDp()));
outputStreamWithNot.writeBytes(ByteUtils.convert16BitIntToBytes(adventureEntry.getBossHp()));
outputStreamWithNot.writeBytes(ByteUtils.convert16BitIntToBytes(adventureEntry.getBossAp()));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.github.cfogrady.vb.dim.adventure;

public class BemAdventureConstants {
public static final int MAX_TABLE_SIZE = 12;
public static final int ROW_SIZE = 11;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.github.cfogrady.vb.dim.adventure;

import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.SuperBuilder;

@Data
@SuperBuilder(toBuilder = true)
@EqualsAndHashCode(callSuper = true)
public class BemAdventureLevels extends AdventureLevels<BemAdventureLevels.BemAdventureLevel> {

@Data
@SuperBuilder(toBuilder = true)
@EqualsAndHashCode(callSuper = true)
public static class BemAdventureLevel extends AdventureLevels.AdventureLevel {
private final int showBossIdentity; //1 shows the boss, 2 shows OS secret character icon
private final int smallAttackId; //40+ is BEM specific attack sprite
private final int bigAttackId; //22+ is BEM specific attack sprite
private final int background1;
private final int background2;
private final int giftCharacterIndex;

public int getBossBp() {
return getBossDp();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.github.cfogrady.vb.dim.adventure;

import com.github.cfogrady.vb.dim.util.ByteOffsetOutputStream;
import com.github.cfogrady.vb.dim.util.RelativeByteOffsetOutputStream;

import java.io.IOException;
import java.io.UncheckedIOException;

public class BemAdventureWriter {
public void writeAdventures(BemAdventureLevels adventureLevels, ByteOffsetOutputStream generalOutputStream) {
try {
RelativeByteOffsetOutputStream relativeOutputStream = new RelativeByteOffsetOutputStream(generalOutputStream);
for(BemAdventureLevels.BemAdventureLevel level : adventureLevels.getLevels()) {
relativeOutputStream.write16BitInt(level.getSteps());
relativeOutputStream.write16BitInt(level.getBossCharacterIndex());
relativeOutputStream.write16BitInt(level.getShowBossIdentity());
relativeOutputStream.write16BitInt(level.getBossDp());
relativeOutputStream.write16BitInt(level.getBossHp());
relativeOutputStream.write16BitInt(level.getBossAp());
relativeOutputStream.write16BitInt(level.getSmallAttackId());
relativeOutputStream.write16BitInt(level.getBigAttackId());
relativeOutputStream.write16BitInt(level.getBackground1());
relativeOutputStream.write16BitInt(level.getBackground2());
relativeOutputStream.write16BitInt(level.getGiftCharacterIndex());
}
} catch(IOException ioe) {
throw new UncheckedIOException(ioe);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package com.github.cfogrady.vb.dim.adventure;

import com.github.cfogrady.vb.dim.util.ByteOffsetInputStream;
import com.github.cfogrady.vb.dim.util.ByteUtils;
import com.github.cfogrady.vb.dim.util.RelativeByteOffsetInputStream;

import java.io.IOException;
import java.io.UncheckedIOException;
import java.util.ArrayList;
import java.util.List;

public class BemAdventuresReader {
public BemAdventureLevels readAdventures(ByteOffsetInputStream generalStream) {
try {
RelativeByteOffsetInputStream relativeInputStream = new RelativeByteOffsetInputStream(generalStream);
List<BemAdventureLevels.BemAdventureLevel> levels = new ArrayList<>(BemAdventureConstants.MAX_TABLE_SIZE);
int[] values = getRowValues(relativeInputStream);
boolean validRow = !ByteUtils.onlyZerosOrMaxValuesInArray(values);
while (validRow) {
levels.add(BemAdventureLevels.BemAdventureLevel.builder()
.steps(values[0])
.bossCharacterIndex(values[1])
.showBossIdentity(values[2])
.bossDp(values[3])
.bossHp(values[4])
.bossAp(values[5])
.smallAttackId(values[6])
.bigAttackId(values[7])
.background1(values[8])
.background2(values[9])
.giftCharacterIndex(values[10])
.build());
values = getRowValues(relativeInputStream);
validRow = !ByteUtils.onlyZerosOrMaxValuesInArray(values);
}
return BemAdventureLevels.builder().levels(levels).build();
} catch (IOException ioe) {
throw new UncheckedIOException(ioe);
}
}

private int[] getRowValues(RelativeByteOffsetInputStream relativeInputStream) throws IOException {
byte[] rowBytes = relativeInputStream.readNBytes(BemAdventureConstants.ROW_SIZE * 2);
return ByteUtils.getUnsigned16Bit(rowBytes);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.github.cfogrady.vb.dim.adventure;

import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.SuperBuilder;

@SuperBuilder(toBuilder=true)
@Data
@EqualsAndHashCode(callSuper = true)
public class DimAdventures extends AdventureLevels<AdventureLevels.AdventureLevel> {
public static final int VB_TABLE_SIZE = 15;
private final int dummyRows;
}
Original file line number Diff line number Diff line change
@@ -1,23 +1,22 @@
package com.github.cfogrady.vb.dim.reader.reader;
package com.github.cfogrady.vb.dim.adventure;

import com.github.cfogrady.vb.dim.reader.ByteUtils;
import com.github.cfogrady.vb.dim.reader.content.DimAdventures;
import com.github.cfogrady.vb.dim.util.ByteUtils;

import java.util.ArrayList;
import java.util.List;

class DimAdventuresReader {
static DimAdventures dimAdventuresFromBytes(byte[] bytes) {
List<DimAdventures.DimAdventureBlock> adventureBlocks = new ArrayList<>(DimAdventures.VB_TABLE_SIZE);
public class DimAdventuresReader {
public static DimAdventures dimAdventuresFromBytes(byte[] bytes) {
List<DimAdventures.AdventureLevel> adventureBlocks = new ArrayList<>(DimAdventures.VB_TABLE_SIZE);
int[] values = ByteUtils.getUnsigned16Bit(bytes);
int index = 0;
boolean onlyZeroRow = ByteUtils.onlyZerosInRange(values, index, 5);
int dummyRows = 0;
while(!onlyZeroRow) {
if (!ByteUtils.onlyZerosOrMaxValuesInRange(values, index, 5)) {
DimAdventures.DimAdventureBlock block = DimAdventures.DimAdventureBlock.builder()
DimAdventures.AdventureLevel block = DimAdventures.AdventureLevel.builder()
.steps(values[index])
.bossStatsIndex(values[index+1])
.bossCharacterIndex(values[index+1])
.bossDp(values[index+2])
.bossHp(values[index+3])
.bossAp(values[index+4])
Expand All @@ -29,6 +28,6 @@ static DimAdventures dimAdventuresFromBytes(byte[] bytes) {
index += 5;
onlyZeroRow = ByteUtils.onlyZerosInRange(values, index, 5); //find out if the next row is only zeros
}
return DimAdventures.builder().adventureBlocks(adventureBlocks).dummyRows(dummyRows).build();
return DimAdventures.builder().levels(adventureBlocks).dummyRows(dummyRows).build();
}
}
22 changes: 22 additions & 0 deletions src/main/java/com/github/cfogrady/vb/dim/card/BemCard.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.github.cfogrady.vb.dim.card;

import com.github.cfogrady.vb.dim.adventure.BemAdventureLevels;
import com.github.cfogrady.vb.dim.character.BemCharacterStats;
import com.github.cfogrady.vb.dim.fusion.AttributeFusions;
import com.github.cfogrady.vb.dim.fusion.BemSpecificFusions;
import com.github.cfogrady.vb.dim.header.BemHeader;
import com.github.cfogrady.vb.dim.transformation.BemTransformationRequirements;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.SuperBuilder;

@Data
@SuperBuilder(toBuilder = true)
@EqualsAndHashCode(callSuper = true)
public class BemCard extends Card<
BemHeader,
BemCharacterStats,
BemTransformationRequirements,
BemAdventureLevels,
AttributeFusions,
BemSpecificFusions> { }
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.github.cfogrady.vb.dim.card;

public class BemCardConstants {
public static final int CHARACTER_SECTION_START = 0x30000;
public static final int TRANSFORMATION_SECTION_START = 0x40000;
public static final int ADVENTURE_SECTION_START = 0x50000;
public static final int SPRITE_DIMENSIONS_START = 0x60000;
public static final int ATTRIBUTE_FUSION_START = 0x70000; //may never be used again
public static final int SPECIFIC_FUSION_START = 0x80000;
public static final int SPRITE_PACKAGE_START = 0x100000;
public static final int CHECKSUM_LOCATION = 0x3FFFFE;

public static final int NONE_VALUE = 0xFFFF;
}
104 changes: 104 additions & 0 deletions src/main/java/com/github/cfogrady/vb/dim/card/BemCardReader.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package com.github.cfogrady.vb.dim.card;


import com.github.cfogrady.vb.dim.adventure.BemAdventureLevels;
import com.github.cfogrady.vb.dim.adventure.BemAdventuresReader;
import com.github.cfogrady.vb.dim.character.BemCharacterReader;
import com.github.cfogrady.vb.dim.character.BemCharacterStats;
import com.github.cfogrady.vb.dim.fusion.AttributeFusions;
import com.github.cfogrady.vb.dim.fusion.BemFusionReader;
import com.github.cfogrady.vb.dim.fusion.BemSpecificFusions;
import com.github.cfogrady.vb.dim.header.BemHeader;
import com.github.cfogrady.vb.dim.header.BemHeaderReader;
import com.github.cfogrady.vb.dim.sprite.BemSpriteReader;
import com.github.cfogrady.vb.dim.sprite.SpriteData;
import com.github.cfogrady.vb.dim.transformation.BemTransformationReader;
import com.github.cfogrady.vb.dim.transformation.BemTransformationRequirements;
import com.github.cfogrady.vb.dim.util.ByteOffsetInputStream;
import com.github.cfogrady.vb.dim.util.ByteUtils;
import com.github.cfogrady.vb.dim.util.DIMChecksumBuilder;
import com.github.cfogrady.vb.dim.util.InputStreamWithNot;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.util.List;

@RequiredArgsConstructor
@Slf4j
public class BemCardReader {
private final BemHeaderReader bemHeaderReader;
private final BemCharacterReader bemCharacterReader;
private final BemTransformationReader bemTransformationReader;
private final BemAdventuresReader bemAdventuresReader;
private final BemFusionReader bemFusionReader;
private final BemSpriteReader bemSpriteReader;

public BemCardReader() {
bemHeaderReader = new BemHeaderReader();
bemCharacterReader = new BemCharacterReader();
bemTransformationReader = new BemTransformationReader();
bemAdventuresReader = new BemAdventuresReader();
bemFusionReader = new BemFusionReader();
bemSpriteReader = new BemSpriteReader();
}

public BemCard readBemCard(InputStream inputStream) {
DIMChecksumBuilder checksumBuilder = new DIMChecksumBuilder();
InputStreamWithNot inputStreamWithNot = new InputStreamWithNot(inputStream, checksumBuilder);
try {
byte[] headerBytes = inputStreamWithNot.readNBytes(0x1030);
return readBemCard(headerBytes, inputStreamWithNot);
} catch (IOException ioe) {
throw new UncheckedIOException(ioe);
}
}

BemCard readBemCard(byte[] headerBytes, InputStreamWithNot inputStream) {
BemHeader header = bemHeaderReader.readBemHeaderFromHeaderBytes(headerBytes);
try {
inputStream.readToOffset(BemCardConstants.CHARACTER_SECTION_START);
BemCharacterStats characters = bemCharacterReader.readCharacterStats(inputStream);
inputStream.readToOffset(BemCardConstants.TRANSFORMATION_SECTION_START);
BemTransformationRequirements transformations = bemTransformationReader.readTransformations(inputStream);
inputStream.readToOffset(BemCardConstants.ADVENTURE_SECTION_START);
BemAdventureLevels adventureLevels = bemAdventuresReader.readAdventures(inputStream);
inputStream.readToOffset(BemCardConstants.SPRITE_DIMENSIONS_START);
List<SpriteData.SpriteDimensions> spriteDimensions = bemSpriteReader.readSpriteDimensions(inputStream);
inputStream.readToOffset(BemCardConstants.ATTRIBUTE_FUSION_START);
AttributeFusions attributeFusions = bemFusionReader.readAttributeFusion(inputStream);
inputStream.readToOffset(BemCardConstants.SPECIFIC_FUSION_START);
BemSpecificFusions specificFusions = bemFusionReader.readSpecificFusions(inputStream);
inputStream.readToOffset(BemCardConstants.SPRITE_PACKAGE_START);
SpriteData spriteData = bemSpriteReader.getSpriteData(inputStream, spriteDimensions);
int checksumOnCard = readToChecksum(inputStream);
int calculatedChecksum = inputStream.getChecksum();
if(checksumOnCard != calculatedChecksum) {
log.warn("Checksums don't match! Calculated: {} Received: {}", Integer.toHexString(calculatedChecksum), Integer.toHexString(checksumOnCard));
}

return BemCard.builder()
.header(header)
.characterStats(characters)
.transformationRequirements(transformations)
.adventureLevels(adventureLevels)
.attributeFusions(attributeFusions)
.specificFusions(specificFusions)
.spriteData(spriteData)
.calculatedCheckSum(calculatedChecksum)
.checksum(checksumOnCard)
.build();
} catch (IOException ioe) {
throw new UncheckedIOException(ioe);
}
}

private int readToChecksum(ByteOffsetInputStream inputStream) throws IOException {
inputStream.readToOffset(BemCardConstants.CHECKSUM_LOCATION);
byte[] checksumBytes = inputStream.readNBytes(2);
int dimChecksum = ByteUtils.getUnsigned16Bit(checksumBytes)[0];
return dimChecksum;
}
}
Loading

0 comments on commit da32c66

Please sign in to comment.