Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SSB Editor should only show for SSB amiibo. #86

Merged
merged 1 commit into from
Sep 2, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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