Skip to content

Commit

Permalink
Merge pull request #86 from HiddenRamblings/show_ssb_editor_only_when…
Browse files Browse the repository at this point in the history
…_compatble

SSB Editor should only show for SSB amiibo.
  • Loading branch information
North101 authored Sep 2, 2017
2 parents 70e43a0 + 569f04f commit e4655e9
Show file tree
Hide file tree
Showing 7 changed files with 209 additions and 113 deletions.
124 changes: 86 additions & 38 deletions app/src/main/java/com/hiddenramblings/tagmo/EditorSSB.java
Original file line number Diff line number Diff line change
@@ -1,23 +1,32 @@
package com.hiddenramblings.tagmo;

import android.app.Activity;
import android.content.DialogInterface;
import android.content.Intent;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.widget.ArrayAdapter;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;

import com.hiddenramblings.tagmo.amiibo.AmiiboSeries;

import org.androidannotations.annotations.AfterViews;
import org.androidannotations.annotations.EActivity;
import org.androidannotations.annotations.OptionsItem;
import org.androidannotations.annotations.OptionsMenu;
import org.androidannotations.annotations.UiThread;
import org.androidannotations.annotations.ViewById;

import java.nio.ByteBuffer;

@EActivity(R.layout.activity_editor_ssb)
@OptionsMenu({R.menu.editor_menu})
public class EditorSSB extends AppCompatActivity {
public static final long SSB_AMIIBO_SERIES = 0x0000000000000000L;
public static final int APP_ID = 0x10110E00;

private static final String TAG = "EditorSSB";

@ViewById(R.id.spnAppearance)
Expand Down Expand Up @@ -61,28 +70,48 @@ public class EditorSSB extends AppCompatActivity {
@AfterViews
void afterViews() {
setListForSpinners(new Spinner[]{spnAppearance},
R.array.ssb_appearance_values, android.R.layout.simple_list_item_1);
R.array.ssb_appearance_values, android.R.layout.simple_list_item_1);
setListForSpinners(new Spinner[]{spnSpecial1, spnSpecial2, spnSpecial3, spnSpecial4},
R.array.ssb_specials_values, android.R.layout.simple_list_item_1);
R.array.ssb_specials_values, android.R.layout.simple_list_item_1);
setListForSpinners(new Spinner[]{spnEffect1, spnEffect2, spnEffect3},
R.array.ssb_bonus_effects, android.R.layout.simple_list_item_1);
R.array.ssb_bonus_effects, android.R.layout.simple_list_item_1);
setListForSpinners(new Spinner[]{spnStatAttack, spnStatDefense, spnStatSpeed},
R.array.ssb_stat_values, android.R.layout.simple_list_item_1);
R.array.ssb_stat_values, android.R.layout.simple_list_item_1);
setListForSpinners(new Spinner[]{spnLevel},
R.array.ssb_levels, android.R.layout.simple_list_item_1);
R.array.ssb_levels, android.R.layout.simple_list_item_1);

keyManager = new KeyManager(this);

try {
byte[] tagData = getIntent().getByteArrayExtra(Actions.EXTRA_TAG_DATA);

long amiiboId;
try {
amiiboId = TagUtil.amiiboIdFromTag(tagData);
} catch (Exception e) {
e.printStackTrace();
LogError("Unable read Amiibo ID");
return;
}

if (!canEditAmiibo(amiiboId)) {
LogError("This Amiibo is not compatible with this editor");
return;
}

tagData = TagUtil.decrypt(keyManager, tagData);
this.loadData(tagData);
} catch (Exception e) {
Log.d(TAG, "Error decrypting data", e);
finish();
e.printStackTrace();

LogError("Error decyrpting data");
}
}

public static boolean canEditAmiibo(long amiiboId) {
return (amiiboId & AmiiboSeries.MASK) == SSB_AMIIBO_SERIES;
}


//all offsets for decrypted (internal) format of tags
private static final int OFFSET_APP_DATA = 0xDC;
Expand All @@ -105,8 +134,8 @@ void afterViews() {
private static final int OFFSET_LEVEL = OFFSET_APP_DATA + 0x7C;

int readStat(byte[] data, int offset) {
short value = (short)((data[offset] & 0xFF) << 8);
value |= data[offset+1] & 0xFF;
short value = (short) ((data[offset] & 0xFF) << 8);
value |= data[offset + 1] & 0xFF;
int res = value;
res += 200; //shift the value to make it positive value as the seek bar doesnt support negative

Expand All @@ -121,8 +150,8 @@ int readStat(byte[] data, int offset) {
void writeStat(byte[] data, int value, int offset) {
value -= 200;

data[offset] = (byte)(((short)value) >> 8);
data[offset+1] = (byte)(((short)value) & 0xFF);
data[offset] = (byte) (((short) value) >> 8);
data[offset + 1] = (byte) (((short) value) & 0xFF);
}

void setEffectValue(Spinner spinner, int value) {
Expand Down Expand Up @@ -161,20 +190,20 @@ void setSpinnerValue(Spinner spinner, int value) {
spinner.setSelection(value);
}

private static final int[] LEVEL_THRESHOLDS = new int[]{ 0x00, 0x08, 0x010, 0x01D, 0x02D, 0x048,
0x05B, 0x075, 0x08D, 0x0AF, 0x0E1, 0x0103, 0x0126, 0x0149, 0x0172, 0x0196, 0x01BE, 0x01F7,
0x0216, 0x0240, 0x0278, 0x02A4, 0x02D6, 0x030E, 0x034C, 0x037C, 0x03BB, 0x03F4, 0x042A, 0x0440,
0x048A, 0x04B6,0x04E3, 0x053F, 0x056D, 0x059C, 0x0606, 0x0641, 0x0670, 0x069E, 0x06FC, 0x072E,
0x075D, 0x07B9, 0x07E7, 0x0844, 0x0875, 0x08D3, 0x0902, 0x093E
private static final int[] LEVEL_THRESHOLDS = new int[]{0x00, 0x08, 0x010, 0x01D, 0x02D, 0x048,
0x05B, 0x075, 0x08D, 0x0AF, 0x0E1, 0x0103, 0x0126, 0x0149, 0x0172, 0x0196, 0x01BE, 0x01F7,
0x0216, 0x0240, 0x0278, 0x02A4, 0x02D6, 0x030E, 0x034C, 0x037C, 0x03BB, 0x03F4, 0x042A, 0x0440,
0x048A, 0x04B6, 0x04E3, 0x053F, 0x056D, 0x059C, 0x0606, 0x0641, 0x0670, 0x069E, 0x06FC, 0x072E,
0x075D, 0x07B9, 0x07E7, 0x0844, 0x0875, 0x08D3, 0x0902, 0x093E
};

int readLevel(byte[] data) {
int value = (data[OFFSET_LEVEL] & 0xFF) << 8;
value |= (data[OFFSET_LEVEL+1] & 0xFF);
value |= (data[OFFSET_LEVEL + 1] & 0xFF);

for (int i= LEVEL_THRESHOLDS.length-1; i>=0; i--) {
for (int i = LEVEL_THRESHOLDS.length - 1; i >= 0; i--) {
if (LEVEL_THRESHOLDS[i] <= value)
return i+1;
return i + 1;
}
return 1;
}
Expand All @@ -191,6 +220,12 @@ void writeLevel(byte[] data, int level) {
}

void loadData(final byte[] data) {
int appId = ByteBuffer.wrap(data, TagUtil.APP_ID_OFFSET, TagUtil.APP_ID_LENGTH).getInt();
if (appId != APP_ID) {
LogError("The Amiibo's app data is not formatted for Super Smash Bros.");
return;
}

try {
setSpinnerValue(spnAppearance, data[OFFSET_APPEARANCE] & 0xFF);

Expand All @@ -208,34 +243,35 @@ void loadData(final byte[] data) {
setEffectValue(spnEffect3, data[OFFSET_BONUS_EFFECT3] & 0xFF);

setSpinnerValue(spnLevel, readLevel(data) - 1);
} catch (Exception ex) {
Log.e(TAG, "Error loading SSB data", ex);
Toast.makeText(this, "Error loading data. Tag may not be a SSB", Toast.LENGTH_LONG);
} catch (Exception e) {
e.printStackTrace();

LogError("Error parsing data");
}
}

void updateData(byte[] data) {
data[OFFSET_APPEARANCE] = (byte)spnAppearance.getSelectedItemPosition();
data[OFFSET_SPECIAL_NEUTRAL] = (byte)spnSpecial1.getSelectedItemPosition();
data[OFFSET_SPECIAL_SIDE_TO_SIDE] = (byte)spnSpecial2.getSelectedItemPosition();
data[OFFSET_SPECIAL_UP] = (byte)spnSpecial3.getSelectedItemPosition();
data[OFFSET_SPECIAL_DOWN] = (byte)spnSpecial4.getSelectedItemPosition();
data[OFFSET_APPEARANCE] = (byte) spnAppearance.getSelectedItemPosition();
data[OFFSET_SPECIAL_NEUTRAL] = (byte) spnSpecial1.getSelectedItemPosition();
data[OFFSET_SPECIAL_SIDE_TO_SIDE] = (byte) spnSpecial2.getSelectedItemPosition();
data[OFFSET_SPECIAL_UP] = (byte) spnSpecial3.getSelectedItemPosition();
data[OFFSET_SPECIAL_DOWN] = (byte) spnSpecial4.getSelectedItemPosition();

writeStat(data, (short)spnStatAttack.getSelectedItemPosition(), OFFSET_STATS_ATTACK);
writeStat(data, (short)spnStatDefense.getSelectedItemPosition(), OFFSET_STATS_DEFENSE);
writeStat(data, (short)spnStatSpeed.getSelectedItemPosition(), OFFSET_STATS_SPEED);
writeStat(data, (short) spnStatAttack.getSelectedItemPosition(), OFFSET_STATS_ATTACK);
writeStat(data, (short) spnStatDefense.getSelectedItemPosition(), OFFSET_STATS_DEFENSE);
writeStat(data, (short) spnStatSpeed.getSelectedItemPosition(), OFFSET_STATS_SPEED);

data[OFFSET_BONUS_EFFECT1] = (byte)getEffectValue(spnEffect1);
data[OFFSET_BONUS_EFFECT2] = (byte)getEffectValue(spnEffect2);
data[OFFSET_BONUS_EFFECT3] = (byte)getEffectValue(spnEffect3);
data[OFFSET_BONUS_EFFECT1] = (byte) getEffectValue(spnEffect1);
data[OFFSET_BONUS_EFFECT2] = (byte) getEffectValue(spnEffect2);
data[OFFSET_BONUS_EFFECT3] = (byte) getEffectValue(spnEffect3);

writeLevel(data, spnLevel.getSelectedItemPosition()+1);
writeLevel(data, spnLevel.getSelectedItemPosition() + 1);
}

void setListForSpinners(Spinner[] controls, int list, int layout) {
for(int i=0; i<controls.length; i++) {
for (Spinner control : controls) {
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this, list, layout);
controls[i].setAdapter(adapter);
control.setAdapter(adapter);
}
}

Expand All @@ -255,5 +291,17 @@ void save() {
finish();
}


@UiThread
void LogError(String msg) {
new AlertDialog.Builder(this)
.setTitle("Error")
.setMessage(msg)
.setPositiveButton("Close", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
EditorSSB.this.finish();
}
})
.show();
}
}
66 changes: 54 additions & 12 deletions app/src/main/java/com/hiddenramblings/tagmo/EditorTP.java
Original file line number Diff line number Diff line change
@@ -1,23 +1,28 @@
package com.hiddenramblings.tagmo;

import android.app.Activity;
import android.content.DialogInterface;
import android.content.Intent;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.widget.ArrayAdapter;
import android.widget.Spinner;
import android.widget.Toast;

import org.androidannotations.annotations.AfterViews;
import org.androidannotations.annotations.EActivity;
import org.androidannotations.annotations.OptionsItem;
import org.androidannotations.annotations.OptionsMenu;
import org.androidannotations.annotations.UiThread;
import org.androidannotations.annotations.ViewById;

import java.nio.ByteBuffer;

@EActivity(R.layout.activity_editor_tp)
@OptionsMenu({R.menu.editor_menu})
public class EditorTP extends AppCompatActivity {
public static final long WOLF_LINK_ID = 0x01030000024F0902L;
public static final int APP_ID = 0x1019C800;

private static final String TAG = "EditorTP";

Expand All @@ -31,45 +36,68 @@ public class EditorTP extends AppCompatActivity {
@AfterViews
void afterViews() {
setListForSpinners(new Spinner[]{spnShadowCaveLevel},
R.array.editor_tp_levels, android.R.layout.simple_list_item_1);
R.array.editor_tp_levels, android.R.layout.simple_list_item_1);
setListForSpinners(new Spinner[]{spnHearts},
R.array.editor_tp_hearts, android.R.layout.simple_list_item_1);
R.array.editor_tp_hearts, android.R.layout.simple_list_item_1);

keyManager = new KeyManager(this);

try {
byte[] tagData = getIntent().getByteArrayExtra(Actions.EXTRA_TAG_DATA);

long amiiboId;
try {
amiiboId = TagUtil.amiiboIdFromTag(tagData);
} catch (Exception e) {
e.printStackTrace();
LogError("Unable read Amiibo ID");
return;
}

if (!canEditAmiibo(amiiboId)) {
LogError("This Amiibo is not compatible with this editor");
return;
}

tagData = TagUtil.decrypt(keyManager, tagData);
this.loadData(tagData);
} catch (Exception e) {
Log.d(TAG, "Error decyrpting data", e);
finish();
e.printStackTrace();

LogError("Error decyrpting data");
}
}

public static boolean canEditAmiibo(long amiiboId) {
return amiiboId == WOLF_LINK_ID;
}

//all offsets for decrypted (internal) format of tags
private static final int OFFSET_APP_DATA = 0xED;

private static final int OFFSET_LEVEL = OFFSET_APP_DATA;
private static final int OFFSET_HEARTS = OFFSET_APP_DATA + 0x01;


void loadData(final byte[] data) {
int appId = ByteBuffer.wrap(data, TagUtil.APP_ID_OFFSET, TagUtil.APP_ID_LENGTH).getInt();
if (appId != APP_ID) {
LogError("This Amiibo's app data is not formatted for The Legend of Zelda: Twilight Princess HD HD Cave of Shadows.");
return;
}

try {
if (data[OFFSET_LEVEL] < 0 || data[OFFSET_LEVEL] > spnShadowCaveLevel.getAdapter().getCount()) {
throw new IndexOutOfBoundsException("OFFSET_LEVEL Value invalid: "+data[OFFSET_LEVEL]);
throw new IndexOutOfBoundsException("OFFSET_LEVEL Value invalid: " + data[OFFSET_LEVEL]);
}
if (data[OFFSET_HEARTS] < 0 || data[OFFSET_HEARTS] > spnHearts.getAdapter().getCount()) {
throw new IndexOutOfBoundsException("OFFSET_HEARTS Value invalid: "+data[OFFSET_HEARTS]);
throw new IndexOutOfBoundsException("OFFSET_HEARTS Value invalid: " + data[OFFSET_HEARTS]);
}

spnShadowCaveLevel.setSelection(data[OFFSET_LEVEL]);
spnHearts.setSelection(data[OFFSET_HEARTS]);
} catch (IndexOutOfBoundsException e) {
Log.d(TAG, "Error loading data", e);
Toast.makeText(this, R.string.editor_error_outofbounds, Toast.LENGTH_SHORT).show();
finish();
e.printStackTrace();

LogError("Error parsing data");
}
}

Expand Down Expand Up @@ -100,4 +128,18 @@ void save() {
}
finish();
}

@UiThread
void LogError(String msg) {
new AlertDialog.Builder(this)
.setTitle("Error")
.setMessage(msg)
.setPositiveButton("Close", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
EditorTP.this.finish();
}
})
.show();
}
}
5 changes: 3 additions & 2 deletions app/src/main/java/com/hiddenramblings/tagmo/MainActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -592,9 +592,10 @@ public void onDismissed(Snackbar snackbar, int event) {
int tpVisibility = View.GONE;
try {
long amiiboId = TagUtil.amiiboIdFromTag(currentTagData);
if (amiiboId == EditorTP.WOLF_LINK_ID) {
if (EditorTP.canEditAmiibo(amiiboId)) {
tpVisibility = View.VISIBLE;
} else {
}
if (EditorSSB.canEditAmiibo(amiiboId)) {
ssbVisibility = View.VISIBLE;
}
} catch (Exception e) {
Expand Down
Loading

0 comments on commit e4655e9

Please sign in to comment.