Skip to content

Commit 2c4b32b

Browse files
committed
fix: optimised database to remove lingering issues on swaps; updated readme to cover remaining issues; deleted some (not all) unused code
1 parent 9057bf0 commit 2c4b32b

8 files changed

Lines changed: 177 additions & 74 deletions

File tree

readme.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ I need to know the version of Java you're using and the output of the log panel
4848

4949
### Known Issues:
5050

51+
1. A fresh install of all card data is required after each install currently [I'm guessing this is some kind of auto refresh function baked into the app]
5152
1. I raced through this so the UI is janky as hell. I expect frequent restarts. Some of it looks pretty bad with the Java 1.8 compilation settings but it'll do
5253
1. Logs aren't updating correctly; I think I need to add some multithreading functionality
5354
1. UI in general has weird lags tbh
@@ -63,8 +64,10 @@ I need to know the version of Java you're using and the output of the log panel
6364
1. Directly installs modified APK onto connected android device (requires ADB server instance)
6465

6566
## Future work:
67+
68+
1. Alternative swap process using `level0` data (will not require fresh data downloads so future-proof but won't have 100% accurate card behaviour)
6669
1. Include associated baffler cards in playlist swaps
67-
1. Streamline process some more (can I launch the ADB server within the jar code?)
70+
1. Mod iOS app on M1 devices
6871
1. Verify working on Windows
6972
1. Tools for straightforward installing of APK without mods
7073
1. Mayyyyybe custom cards? This involves modifying the level0 assets file instead and injecting custom data files post-install so it's not so easy

src/model/AppState.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,9 +110,16 @@ byte[] loadFile() {
110110
throw new Error(e);
111111
}
112112
}
113+
public void removePlaylistSwap(String p1) {
114+
String p2 = this.playlistSwap.get(p1);
115+
this.playlistSwap.remove(p1);
116+
this.playlistSwap.remove(p2);
117+
}
113118
public void setPlaylistSwap(String p1, String p2) throws Exception {
114119
if (p1.contains("---") || p2.contains("---")) {
115120
// for handling reset of field
121+
this.playlistSwap.remove(p1);
122+
this.playlistSwap.remove(p2);
116123
return;
117124
}
118125
if (this.playlistSwap.get(p1) != null || this.playlistSwap.get(p2) != null) {

src/model/CardDetail.java

Lines changed: 28 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -93,40 +93,43 @@ public String getCardId() {
9393
private void setCardId(String newId) {
9494
this.cardData.put(CardDetail.SourceCID, newId);
9595
}
96-
public String getCardSeason() {
97-
return this.cardData.get(CardDetail.Season);
96+
public int getCardSeason() {
97+
try {
98+
return Integer.parseInt(
99+
this.cardData.get(CardDetail.Season).replaceAll("\"", "")
100+
);
101+
} catch (Exception e) {
102+
return 0;
103+
}
98104
}
99-
public void setSourceCID(String newCardId, boolean preserveLength) {
100-
String currentId = getCardId();
101-
int songTitleLengthChangeRequired = currentId.length() - newCardId.length();
102-
if (!preserveLength && songTitleLengthChangeRequired != 0) {
103-
/*
105+
private void shortedDBRowForModification(int changeRequired) {
106+
/*
104107
SongRef would be preferable here as it will generally be longer but may cause issues with FX cards
105108
With this in mind it makes sense to use the Artist name instead
106109
TODO Investigate whether FX songRef values need to be swapped over when swap is applied
107110
*/
108-
String fieldToChange =
109-
songTitleLengthChangeRequired < 0
110-
&& this.cardData.get(CardDetail.SongRef).length() < Math.abs(songTitleLengthChangeRequired) + 1
111-
? CardDetail.SongRef
112-
: CardDetail.ArtistRef;
113-
String songTitle = this.cardData.get(fieldToChange);
114-
if (songTitleLengthChangeRequired > 0) {
115-
songTitle = Helpers.rPad(songTitle, songTitle.length() + Math.abs(songTitleLengthChangeRequired), ' ');
116-
} else {
117-
songTitle = songTitle.substring(0, songTitle.length() - Math.abs(songTitleLengthChangeRequired) );
118-
}
119-
this.cardData.put(fieldToChange, songTitle);
111+
String fieldToChange =
112+
changeRequired < 0
113+
&& this.cardData.get(CardDetail.SongRef).length() < Math.abs(changeRequired) + 1
114+
? CardDetail.SongRef
115+
: CardDetail.ArtistRef;
116+
String songTitle = this.cardData.get(fieldToChange);
117+
if (changeRequired > 0) {
118+
songTitle = Helpers.rPad(songTitle, songTitle.length() + Math.abs(changeRequired), ' ');
119+
} else {
120+
songTitle = songTitle.substring(0, songTitle.length() - Math.abs(changeRequired) );
120121
}
121-
this.setCardId(newCardId);
122+
this.cardData.put(fieldToChange, songTitle);
122123
}
123-
public String rowToDataString() {
124-
ArrayList<String> sb = new ArrayList<>();
125-
for (String h: heading) {
126-
sb.add(cardData.get(h));
124+
public void setSourceCID(String newCardId, boolean preserveLength) {
125+
String currentId = getCardId();
126+
int songTitleLengthChangeRequired = currentId.length() - newCardId.length();
127+
if (!preserveLength && songTitleLengthChangeRequired != 0) {
128+
shortedDBRowForModification(songTitleLengthChangeRequired);
127129
}
128-
return String.join(",", sb);
130+
this.setCardId(newCardId);
129131
}
132+
130133
public String toString() {
131134
StringBuilder sb = new StringBuilder();
132135
for (String heading: CardDetail.heading) {

src/model/DropmixSharedAssets.java

Lines changed: 34 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -63,44 +63,26 @@ public byte[] applySwap(TreeMap<String, String> cardSwaps) {
6363
CardDetail c2 = modifiedAssets.cards.get(s2);
6464
String c1CardId = c1.getCardId();
6565
String c2CardId = c2.getCardId();
66+
6667
if (!alreadySwapped.contains(c1CardId) && !alreadySwapped.contains(c2CardId)) {
68+
69+
changedSeasons.add("" + c1.getCardSeason());
70+
changedSeasons.add("" + c2.getCardSeason());
6771
// if both are from the same season there's no need to worry about the output table length
68-
boolean isSameSeason = c1.getCardSeason().equals(c2.getCardSeason());
72+
boolean isSameSeason = c1.getCardSeason() == (c2.getCardSeason());
6973
c1.setSourceCID(c2CardId, isSameSeason);
7074
c2.setSourceCID(c1CardId, isSameSeason);
7175
alreadySwapped.add(c1CardId);
7276
alreadySwapped.add(c2CardId);
73-
changedSeasons.add(c1.getCardSeason());
74-
changedSeasons.add(c2.getCardSeason());
7577
}
7678
});
7779
Counter databaseModified = new Counter(0);
7880
Counter databaseSize = new Counter(0);
7981
modifiedAssets.seasons.forEach(new BiConsumer<Integer, SeasonTable>() {
8082
@Override
8183
public void accept(Integer seasonIdx, SeasonTable seasonTable) {
82-
String seasonArray = SeasonTable.csvWriter(seasonTable.toNestedString(), ",", "\"", seasonIdx);
83-
byte[] seasonByteArray = new byte[seasonArray.length() + 4];
84-
for (int i = 0; i < 4; i++) {
85-
seasonByteArray[i] = seasonTable.rawDb[i];
86-
}
87-
char[] seasonCharArray = seasonArray.toCharArray();
88-
for (int i = 0; i < seasonArray.length(); i++) {
89-
seasonByteArray[i + 4] = (byte) seasonCharArray[i];
90-
}
91-
if (seasonByteArray.length != seasonTable.length) {
92-
if (seasonIdx == 0) {
93-
StringBuilder sb = new StringBuilder();
94-
for (byte b: seasonTable.rawDb) {
95-
sb.append((char) b);
96-
}
97-
}
84+
byte[] seasonByteArray = seasonTable.backToByteArray(false);
9885

99-
// TODO be consistent about lengths and start indexes
100-
if (seasonByteArray.length != seasonTable.rawDb.length) {
101-
throw new RuntimeException("modified-database-size-wrong:" + seasonByteArray.length + " " + seasonTable.rawDb.length);
102-
}
103-
}
10486
databaseSize.iterate(seasonTable.length);
10587
for (int i = 0; i < seasonByteArray.length; i++) {
10688
int currentIdx = seasonTable.startIdx + i;
@@ -112,6 +94,34 @@ public void accept(Integer seasonIdx, SeasonTable seasonTable) {
11294
}
11395
});
11496
System.out.printf("Updated %d Seasons; %d of %d (%.2f) bytes", changedSeasons.size(), databaseModified.getCounter(), databaseSize.getCounter(), ((double) databaseModified.getCounter() / (double) databaseSize.getCounter()));
97+
int sCount = 0;
98+
for (SeasonTable s: modifiedAssets.seasons.values()) {
99+
int size = s.length;
100+
StringBuilder start = new StringBuilder();
101+
StringBuilder end = new StringBuilder();
102+
StringBuilder all = new StringBuilder();
103+
StringBuilder old = new StringBuilder();
104+
int printRange = 1600;
105+
if (s.length < printRange) {
106+
continue;
107+
}
108+
for (int i = 0; i < printRange; i++) {
109+
all.append((char) s.rawDb[i]);
110+
}
111+
SeasonTable oldS= seasons.get(sCount);
112+
for (int i = 0; i < size; i++) {
113+
try {
114+
all.append((char) s.rawDb[i]);
115+
old.append((char) oldS.rawDb[i]);
116+
} catch (ArrayIndexOutOfBoundsException | NullPointerException e) {}
117+
}
118+
}
119+
DropmixSharedAssets ds = new DropmixSharedAssets(clonedAssetsFile);
120+
for (int i = 0; i < 3; i++) {
121+
SeasonTable s1 = modifiedAssets.seasons.get(i);
122+
SeasonTable s2 = ds.seasons.get(i);
123+
System.out.printf("\nSize: %d %d; db len: %d %d", s1.length, s2.length, s1.rawDb.length, s2.rawDb.length);
124+
}
115125
return clonedAssetsFile;
116126
}
117127
public static int getStartIndex(byte[] rawData, byte[] startSequence) {

src/model/DropmixSharedAssetsTest.java

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,56 @@ void applySwap() {
5454
Assertions.assertNotEquals(moddedFile, dropmixSharedAssets.rawData);
5555
}
5656

57+
@Test
58+
void applySwapOfPlaylists() {
59+
60+
AppState as = AppState.getInstance();
61+
as.assetsHandler = dropmixSharedAssets;
62+
TreeMap<String, String> playlists = new TreeMap<>();
63+
String[] names = new String[]{"derby", "ouroboros", "mirrors", "astro", "energy", "city", "flora", "verdant", "rhymer", "phase", "instinct", "fantastic" };
64+
for (int i=0; i< names.length; i++) {
65+
playlists.put(names[i], names[names.length - 1 -i]);
66+
}
67+
TreeMap<String, String> swapCards = AppState.getCardSwapFromPlaylist(playlists);
68+
69+
int oldLength = dropmixSharedAssets.rawData.length;
70+
byte[] moddedFile = dropmixSharedAssets.applySwap(swapCards);
71+
DropmixSharedAssets newVersion = new DropmixSharedAssets(moddedFile);
72+
Assertions.assertEquals(moddedFile.length, oldLength);
73+
Assertions.assertNotEquals(moddedFile, dropmixSharedAssets.rawData);
74+
for (int i = 0; i < 3; i++) {
75+
SeasonTable s1 = dropmixSharedAssets.seasons.get(i);
76+
SeasonTable s2 = newVersion.seasons.get(i);
77+
byte[] original = Helpers.getNRange(s1.rawDb, 4, s1.rawDb.length - 4);
78+
byte[] modified = s2.backToByteArray(i == 2);
79+
Assertions.assertEquals(s1.length, s2.length);
80+
Assertions.assertEquals(s1.startIdx, s2.startIdx);
81+
Assertions.assertNotEquals(original, modified);
82+
int range = 80;
83+
byte[] endRangeA = Helpers.getNRange(original, original.length - range, range);
84+
byte[] endRangeB = Helpers.getNRange(modified, modified.length - range, range);
85+
int lastIdx = 0;
86+
for (int k = 0; k < original.length; k++) {
87+
if (original[k] != modified[k] && (k - lastIdx) > range) {
88+
lastIdx = k;
89+
byte[] r1 = Helpers.getNRange(original, k - range, range * 2);
90+
byte[] r2 = Helpers.getNRange(modified, k - range, range * 2);
91+
StringBuilder a1 = new StringBuilder();
92+
StringBuilder a2 = new StringBuilder();
93+
for (int l = 0; l < range*2; l++) {
94+
a1.append((char) r1[l]);
95+
a2.append((char) r2[l]);
96+
}
97+
System.out.println(a1);
98+
System.out.println(a2);
99+
return;
100+
}
101+
}
102+
System.out.println(endRangeA[0]);
103+
System.out.println(endRangeB[0]);
104+
Assertions.assertArrayEquals(endRangeA, endRangeB);
105+
}
106+
}
57107
@Test
58108
void replaceRogueCommas() {
59109
String rt = "\"Flo Rida\",\"I Don't Like It, I Love It (ft. Robin Thicke, Verdine White)\",\"Loop\"";

src/model/SeasonTable.java

Lines changed: 29 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -38,27 +38,29 @@ public SeasonTable(byte[] rawTableData, byte[] startHeader, int startIdx, int se
3838
}
3939
}
4040

41-
public byte[] backToByteArray() {
42-
byte[] newByteArray = this.rawDb.clone();
43-
44-
int counter = 4;
45-
46-
String heading = String.join(",", this.columns);
47-
for (int i = 0; i < heading.length(); i++) {
48-
newByteArray[counter] = (byte) heading.charAt(i);
49-
counter++;
41+
public byte[] backToByteArray(boolean includeEndNewLine) {
42+
String seasonCSV = SeasonTable.csvWriter(toNestedString(), ",", "\"", cards[0].getCardSeason());
43+
byte[] seasonByteArray = new byte[rawDb.length];
44+
// insert DB length
45+
for (int i = 0; i < 4; i++) {
46+
seasonByteArray[i] = rawDb[i];
5047
}
51-
for (CardDetail card: this.cards) {
52-
String cardRow = card.rowToDataString();
53-
for (int i = 0; i < cardRow.length(); i++) {
54-
newByteArray[counter] = (byte) cardRow.charAt(i);
55-
counter++;
56-
}
48+
System.out.println("size diff: " + rawDb.length + ", " + seasonCSV.length());
49+
// build out byte array
50+
for (int i = 0; i < seasonCSV.length(); i++) {
51+
seasonByteArray[i + 4] = (byte) seasonCSV.charAt(i);
52+
}
53+
for (int i = seasonCSV.length() + 4; i < rawDb.length; i++) {
54+
seasonByteArray[i] = rawDb[i];
5755
}
58-
if (newByteArray.length != this.rawDb.length) {
59-
throw new Error("database-length-error");
56+
return seasonByteArray;
57+
}
58+
static String getDb(byte[] b) {
59+
StringBuilder sb = new StringBuilder();
60+
for (byte a: b) {
61+
sb.append((char) a);
6062
}
61-
return newByteArray;
63+
return sb.toString();
6264
}
6365
/**/
6466
public String[][] toNestedString() {
@@ -126,6 +128,10 @@ public static String csvWriter(String[][] csv, String delimiter, String textQual
126128
for (String[] row: csv) {
127129
int currentColIdx = 0;
128130
for (String cell: row) {
131+
boolean hasComma = cell.contains("£") || cell.contains(",");
132+
if (cell.contains("ays Ahead")) {
133+
System.out.println(cell);
134+
}
129135
String formattedString = cell.replaceAll("£", ",");
130136
boolean isLikelyString = true;
131137
try {
@@ -137,11 +143,11 @@ public static String csvWriter(String[][] csv, String delimiter, String textQual
137143
}
138144
}
139145
if (
140-
currentRowIdx != 0
141-
&& (
142-
(isLikelyString && allStringsTextQualifier)
143-
|| (isSeasonFieldString && Objects.equals(csv[0][currentColIdx], CardDetail.Season))
144-
)
146+
(currentRowIdx != 0
147+
&& (
148+
(isLikelyString && allStringsTextQualifier)
149+
|| (isSeasonFieldString && Objects.equals(csv[0][currentColIdx], CardDetail.Season))
150+
)) || (hasComma)
145151
){
146152
formattedString = textQualifier + formattedString + textQualifier;
147153
}

src/ui/UIPlaylistsPanel.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,13 @@ public JComboBox<String> getPlaylistComboBox(String playlist) {
8585
public void itemStateChanged(ItemEvent event) {
8686
if (event.getStateChange() == ItemEvent.SELECTED) {
8787
try {
88-
AppState.getInstance().setPlaylistSwap(playlist, event.getItem().toString());
88+
System.out.println(event.getItem() + "<-->" + playlist);
89+
// clear both fields if this value is selected
90+
if (event.getItem().toString().contains("---")) {
91+
AppState.getInstance().removePlaylistSwap(playlist);
92+
} else {
93+
AppState.getInstance().setPlaylistSwap(playlist, event.getItem().toString());
94+
}
8995
// box.setSelectedItem(event.getItem());
9096
oldSelectionItem = event.getItem();
9197
that.setGrid();

src/util/Helpers.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,24 @@ public static String rPad(String str, int length, char car) {
3434
public static byte[] get4Range(byte[] field, int idx) {
3535
return Arrays.copyOfRange(field, idx, idx + 4);
3636
}
37+
public static byte[] getNRange(byte[] field, int idx, int len) {
38+
return Arrays.copyOfRange(field, idx, idx + len);
39+
}
40+
public static char[] byteToChars(byte[] field) {
41+
char[] chars = new char[field.length];
42+
int i = 0;
43+
for (byte b: field) {
44+
chars[i++] = (char) b;
45+
}
46+
return chars;
47+
}
48+
public static String bytetoString(byte[] field) {
49+
StringBuilder sb = new StringBuilder();
50+
for (byte b: field) {
51+
sb.append((char) b);
52+
}
53+
return sb.toString();
54+
}
3755

3856
public static byte[] loadLocalFile(String strPath) {
3957
try {

0 commit comments

Comments
 (0)