mirror of
https://github.com/kwsch/PKHeX.git
synced 2026-05-04 04:27:04 -05:00
* Update to .NET 10 * Property fields * API signature updates * Extension method blocks * Completed dark mode support Outside of my control: - vertical tab control (pkm editor) - datetimepicker controls - lgpe event flags (no idea) - some control types having white-borders when they should really be gray Box background is 50% transparency to effectively darken the image. * Custom legality report popup * Event diff dialog, version select dialog * Add quick overwrite popup for export sav * Extension methods * Dark Mode: glow currently editing sprite * Add invalid encounter hint for trade evolutions * Extension properties * Append legality hint on hover card * Slot image loading: clear the screen-reader description if a slot is empty/invalid, rather than retain the previous description. Changing boxes would easily confuse users on this.
1109 lines
37 KiB
C#
1109 lines
37 KiB
C#
using System;
|
|
using System.Drawing;
|
|
using System.Drawing.Imaging;
|
|
using System.Globalization;
|
|
using System.Linq;
|
|
using System.Windows.Forms;
|
|
using PKHeX.Core;
|
|
using static System.Buffers.Binary.BinaryPrimitives;
|
|
using static PKHeX.WinForms.PoketchDotMatrix;
|
|
|
|
namespace PKHeX.WinForms;
|
|
|
|
public partial class SAV_Misc4 : Form
|
|
{
|
|
private readonly SAV4 Origin;
|
|
private readonly SAV4 SAV;
|
|
private readonly Hall4? Hall;
|
|
private readonly Record4 Record;
|
|
|
|
private readonly string[] seals, accessories, backdrops, poketchapps;
|
|
private readonly string[] backdropsSorted;
|
|
|
|
public SAV_Misc4(SAV4 sav)
|
|
{
|
|
InitializeComponent();
|
|
WinFormsUtil.TranslateInterface(this, Main.CurrentLanguage);
|
|
SAV = (SAV4)(Origin = sav).Clone();
|
|
|
|
seals = GameInfo.Strings.seals;
|
|
accessories = GameInfo.Strings.accessories;
|
|
backdrops = GameInfo.Strings.backdrops;
|
|
poketchapps = GameInfo.Strings.poketchapps;
|
|
backdropsSorted = [.. backdrops.Order()]; // sorted copy
|
|
|
|
StatNUDA = [NUD_Stat0, NUD_Stat1, NUD_Stat2, NUD_Stat3];
|
|
StatLabelA = [L_Stat0, L_Stat1, L_Stat2, L_Stat3]; // Current, Trade, Record, Trade
|
|
StatRBA = [RB_Stats3_01, RB_Stats3_02];
|
|
HallNUDA =
|
|
[
|
|
NUD_HallType01, NUD_HallType02, NUD_HallType03, NUD_HallType04, NUD_HallType05, NUD_HallType06,
|
|
NUD_HallType07, NUD_HallType08, NUD_HallType09, NUD_HallType10, NUD_HallType11, NUD_HallType12,
|
|
NUD_HallType13, NUD_HallType14, NUD_HallType15, NUD_HallType16, NUD_HallType17,
|
|
];
|
|
PrintButtonA = [BTN_PrintTower, BTN_PrintFactory, BTN_PrintHall, BTN_PrintCastle, BTN_PrintArcade];
|
|
|
|
Record = SAV.Records;
|
|
switch (sav)
|
|
{
|
|
case SAV4DP:
|
|
L_CurrentMap.Visible = CB_UpgradeMap.Visible = false;
|
|
GB_Prints.Visible = GB_Prints.Enabled = GB_Hall.Visible = GB_Hall.Enabled = GB_Castle.Visible = GB_Castle.Enabled = false;
|
|
BFF = [
|
|
[0, 1, 0x5FCA, 0x04, 0x6601],
|
|
];
|
|
break;
|
|
case SAV4Pt:
|
|
L_CurrentMap.Visible = CB_UpgradeMap.Visible = false;
|
|
PrintIndexStart = 79;
|
|
BFF = [
|
|
[0, 1, 0x68E0, 0x04, 0x723D],
|
|
[1, 0, 0x68F4, 0x10, 0x7EF8],
|
|
[0, 0, 0x6924, 0x18, 0x7EFC],
|
|
[2, 0, 0x696C, 0x10, 0x7F00],
|
|
[0, 0, 0x699C, 0x04, 0x7F04],
|
|
];
|
|
Hall = SAV.GetHall();
|
|
break;
|
|
case SAV4HGSS:
|
|
GB_Poketch.Visible = false;
|
|
PrintIndexStart = 77;
|
|
BFF = [
|
|
// { BFV, BFT, addr, 1BFTlen, checkBit
|
|
[0, 1, 0x5264, 0x04, 0x5BC1],
|
|
[1, 0, 0x5278, 0x10, 0x687C],
|
|
[0, 0, 0x52A8, 0x18, 0x6880],
|
|
[2, 0, 0x52F0, 0x10, 0x6884],
|
|
[0, 0, 0x5320, 0x04, 0x6888],
|
|
];
|
|
Hall = SAV.GetHall();
|
|
break;
|
|
default:
|
|
throw new ArgumentOutOfRangeException(nameof(sav), sav, null);
|
|
}
|
|
ReadMain();
|
|
ReadBattleFrontier();
|
|
if (SAV is SAV4Sinnoh s)
|
|
{
|
|
TC_Misc.Controls.Remove(TAB_Walker);
|
|
poffinCase4Editor1.Initialize(s);
|
|
TC_Misc.Controls.Remove(Tab_PokeGear);
|
|
}
|
|
else if (SAV is SAV4HGSS hgss)
|
|
{
|
|
pokeGear4Editor1.Initialize(hgss);
|
|
TC_Misc.Controls.Remove(Tab_Poffins);
|
|
}
|
|
}
|
|
|
|
private void B_Save_Click(object sender, EventArgs e)
|
|
{
|
|
SaveMain();
|
|
SaveBattleFrontier();
|
|
if (SAV is SAV4HGSS)
|
|
pokeGear4Editor1.Save();
|
|
else if (SAV is SAV4Sinnoh)
|
|
poffinCase4Editor1.Save();
|
|
|
|
Origin.CopyChangesFrom(SAV);
|
|
Close();
|
|
}
|
|
|
|
private void B_Cancel_Click(object sender, EventArgs e) => Close();
|
|
|
|
private const int FlyFlagStart = 2480;
|
|
private static ReadOnlySpan<byte> FlyWorkFlagSinnoh => [000, 001, 002, 003, 004, 005, 006, 007, 008, 009, 010, 011, 012, 013, 014, 015, 016, 017, 067, 068];
|
|
private static ReadOnlySpan<byte> LocationIDsSinnoh => [001, 002, 003, 004, 005, 082, 083, 006, 007, 008, 009, 010, 011, 012, 013, 014, 054, 081, 055, 015];
|
|
private static ReadOnlySpan<byte> FlyWorkFlagHGSS => [000, 001, 002, 003, 004, 005, 006, 007, 008, 009, 010, 011, 012, 013, 014, 015, 016, 017, 018, 019, 020, 021, 022, 027, 030, 033, 035];
|
|
private static ReadOnlySpan<byte> LocationIDsHGSS => [138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 229, 227, 221, 225];
|
|
|
|
private void ReadMain()
|
|
{
|
|
NUD_Coin.Maximum = SAV.MaxCoins;
|
|
NUD_Coin.Value = Math.Clamp(SAV.Coin, 0, SAV.MaxCoins);
|
|
NUD_BP.Value = Math.Clamp(SAV.BP, 0, 9999);
|
|
|
|
var locations = SAV is SAV4Sinnoh ? LocationIDsSinnoh : LocationIDsHGSS;
|
|
var flags = SAV is SAV4Sinnoh ? FlyWorkFlagSinnoh : FlyWorkFlagHGSS;
|
|
|
|
CLB_FlyDest.Items.Clear();
|
|
for (int i = 0; i < locations.Length; i++)
|
|
{
|
|
var flagIndex = FlyFlagStart + flags[i];
|
|
var state = SAV.GetEventFlag(flagIndex);
|
|
|
|
var locationID = locations[i];
|
|
var name = GameInfo.Strings.Gen4.Met0[locationID];
|
|
CLB_FlyDest.Items.Add(name, state);
|
|
}
|
|
|
|
if (SAV is SAV4Sinnoh sinnoh)
|
|
{
|
|
ReadPoketch(sinnoh);
|
|
NUD_UGFlags.Value = Math.Clamp(sinnoh.UG_FlagsCaptured, 0, SAV4Sinnoh.UG_MAX);
|
|
L_PokeathlonPoints.Visible = NUD_PokeathlonPoints.Visible = false;
|
|
}
|
|
else if (SAV is SAV4HGSS hgss)
|
|
{
|
|
ReadWalker(hgss);
|
|
ReadPokeathlon(hgss);
|
|
L_UGFlags.Visible = NUD_UGFlags.Visible = false;
|
|
ReadOnlySpan<string> items = ["Map Johto", "Map Johto+", "Map Johto & Kanto"];
|
|
var index = hgss.MapUnlockState;
|
|
if (index >= MapUnlockState4.Invalid)
|
|
index = MapUnlockState4.JohtoKanto;
|
|
foreach (var item in items)
|
|
CB_UpgradeMap.Items.Add(item);
|
|
CB_UpgradeMap.SelectedIndex = (int)index;
|
|
}
|
|
|
|
ReadSeals();
|
|
ReadAccessories();
|
|
ReadBackdrops();
|
|
ReadRecord();
|
|
}
|
|
|
|
private void SaveMain()
|
|
{
|
|
SAV.Coin = (uint)NUD_Coin.Value;
|
|
SAV.BP = (ushort)NUD_BP.Value;
|
|
|
|
var flags = SAV is SAV4Sinnoh ? FlyWorkFlagSinnoh : FlyWorkFlagHGSS;
|
|
for (int i = 0; i < CLB_FlyDest.Items.Count; i++)
|
|
{
|
|
var index = FlyFlagStart + flags[i];
|
|
SAV.SetEventFlag(index, CLB_FlyDest.GetItemChecked(i));
|
|
}
|
|
|
|
if (SAV is SAV4Sinnoh sinnoh)
|
|
{
|
|
SavePoketch(sinnoh);
|
|
sinnoh.UG_FlagsCaptured = (uint)NUD_UGFlags.Value;
|
|
}
|
|
else if (SAV is SAV4HGSS hgss)
|
|
{
|
|
SaveWalker(hgss);
|
|
SavePokeathlon(hgss);
|
|
hgss.MapUnlockState = (MapUnlockState4)CB_UpgradeMap.SelectedIndex;
|
|
}
|
|
|
|
SaveSeals();
|
|
SaveAccessories();
|
|
SaveBackdrops();
|
|
SaveRecord();
|
|
}
|
|
|
|
private void B_AllFlyDest_Click(object sender, EventArgs e)
|
|
{
|
|
for (int i = 0; i < CLB_FlyDest.Items.Count; i++)
|
|
CLB_FlyDest.SetItemChecked(i, true);
|
|
}
|
|
|
|
#region Poketch
|
|
private byte[] DotArtistByte = [];
|
|
|
|
private void ReadPoketch(SAV4Sinnoh s)
|
|
{
|
|
CB_CurrentApp.Items.Clear();
|
|
CLB_Poketch.Items.Clear();
|
|
for (PoketchApp i = 0; i <= PoketchApp.Alarm_Clock; i++)
|
|
{
|
|
var name = poketchapps[(int)i];
|
|
var title = $"{(int)i:00} - {name}";
|
|
CB_CurrentApp.Items.Add(name);
|
|
var value = s.GetPoketchAppUnlocked(i);
|
|
CLB_Poketch.Items.Add(title, value);
|
|
}
|
|
CB_CurrentApp.SelectedIndex = s.CurrentPoketchApp;
|
|
|
|
DotArtistByte = s.GetPoketchDotArtistData();
|
|
SetPictureBoxFromFlags(DotArtistByte);
|
|
const string tip = """
|
|
Guide about D&D ImageFile Format
|
|
width = 24px
|
|
height = 20px
|
|
used color count <= 4
|
|
file size < 2058byte
|
|
""";
|
|
tip1.SetToolTip(PB_DotArtist, tip);
|
|
TAB_Main.AllowDrop = true;
|
|
}
|
|
|
|
private void SavePoketch(SAV4Sinnoh s)
|
|
{
|
|
int unlockedCount = 0;
|
|
s.CurrentPoketchApp = (sbyte)CB_CurrentApp.SelectedIndex;
|
|
for (int i = 0; i < CLB_Poketch.Items.Count; i++)
|
|
{
|
|
var b = CLB_Poketch.GetItemChecked(i);
|
|
s.SetPoketchAppUnlocked((PoketchApp)i, b);
|
|
if (b) unlockedCount++;
|
|
}
|
|
s.SetPoketchDotArtistData(DotArtistByte);
|
|
s.PoketchUnlockedCount = (byte)unlockedCount;
|
|
}
|
|
|
|
private void SetPictureBoxFromFlags(ReadOnlySpan<byte> inp)
|
|
{
|
|
if (inp.Length != DotMatrixPixelCount / 4)
|
|
return;
|
|
var img = GetDotArt(inp);
|
|
PB_DotArtist.Image = img;
|
|
PB_DotArtist.Size = new(img.Width + 2, img.Height + 2); // 1px border
|
|
}
|
|
|
|
private void SetFlagsFromFileName(string fileName)
|
|
{
|
|
var dest = DotArtistByte;
|
|
TryBuild(fileName, dest);
|
|
}
|
|
|
|
private void SetFlagsFromClickPoint(int inpX, int inpY)
|
|
{
|
|
inpX = Math.Clamp(inpX, 0, (DotMatrixWidth * DotMatrixUpscaleFactor) - 1);
|
|
inpY = Math.Clamp(inpY, 0, (DotMatrixHeight * DotMatrixUpscaleFactor) - 1);
|
|
int i = (inpX >> 2) + (DotMatrixWidth * (inpY >> 2));
|
|
Span<byte> ndab = stackalloc byte[DotMatrixPixelCount / 4];
|
|
DotArtistByte.CopyTo(ndab);
|
|
|
|
byte c = (byte)((ndab[i >> 2] >> ((i % 4) << 1)) & 3);
|
|
if (++c >= 4)
|
|
c = 0;
|
|
|
|
ndab[i >> 2] &= (byte)~(3 << ((i % 4) << 1));
|
|
ndab[i >> 2] |= (byte)((c & 3) << ((i % 4) << 1));
|
|
|
|
ndab.CopyTo(DotArtistByte);
|
|
}
|
|
|
|
private void B_GiveAll_Click(object sender, EventArgs e)
|
|
{
|
|
// foreach (CheckBox c in Apps) c.Checked = true;
|
|
for (int i = 0; i < CLB_Poketch.Items.Count; i++)
|
|
CLB_Poketch.SetItemChecked(i, true);
|
|
System.Media.SystemSounds.Asterisk.Play();
|
|
}
|
|
|
|
private void TAB_Poketch_DragEnter(object? sender, DragEventArgs? e)
|
|
{
|
|
if (e?.Data is null)
|
|
return;
|
|
if (TAB_Main.AllowDrop && e.Data.GetDataPresent(DataFormats.FileDrop))
|
|
e.Effect = DragDropEffects.Copy;
|
|
else
|
|
e.Effect = DragDropEffects.None;
|
|
}
|
|
|
|
private void TAB_Poketch_DragDrop(object? sender, DragEventArgs? e)
|
|
{
|
|
if (!TAB_Main.AllowDrop)
|
|
return;
|
|
if (e?.Data?.GetData(DataFormats.FileDrop) is not string[] { Length: not 0 } files)
|
|
return;
|
|
SetFlagsFromFileName(files[0]);
|
|
SetPictureBoxFromFlags(DotArtistByte);
|
|
}
|
|
|
|
private void PB_DotArtist_MouseClick(object sender, MouseEventArgs e)
|
|
{
|
|
SetFlagsFromClickPoint(e.X, e.Y);
|
|
SetPictureBoxFromFlags(DotArtistByte);
|
|
}
|
|
#endregion
|
|
|
|
#region BattleFrontier
|
|
private readonly RadioButton[] StatRBA;
|
|
private readonly NumericUpDown[] StatNUDA;
|
|
private readonly Label[] StatLabelA;
|
|
private readonly NumericUpDown[] HallNUDA;
|
|
private readonly Button[] PrintButtonA;
|
|
private readonly int[][] BFF;
|
|
private readonly int PrintIndexStart;
|
|
|
|
private bool editing;
|
|
private string[][] BFT = null!;
|
|
private int[][] BFV = null!;
|
|
private bool HallStatUpdated;
|
|
|
|
private void ReadBattleFrontier()
|
|
{
|
|
BFV = [
|
|
[2, 0], // Max, Current
|
|
[2, 0, 3, 1], // Max, Current, Max(Trade), Current(Trade)
|
|
[2, 0, 1, -1, 3], // Max, Current, Current(CP), (UsedCP), Max(CP)
|
|
];
|
|
BFT = [
|
|
["Singles", "Doubles", "Multi"],
|
|
["Singles", "Doubles", "Multi (Trainer)", "Multi (Friend)", "Wi-Fi"],
|
|
];
|
|
|
|
if (SAV is not SAV4DP)
|
|
{
|
|
SetPrintColors(PrintButtonA);
|
|
|
|
ReadOnlySpan<string> typeNames = GameInfo.Strings.types;
|
|
ReadOnlySpan<byte> typenameIndex = [0, 9, 10, 12, 11, 14, 1, 3, 4, 2, 13, 6, 5, 7, 15, 16, 8];
|
|
for (int i = 0; i < HallNUDA.Length; i++)
|
|
tip2.SetToolTip(HallNUDA[i], typeNames[typenameIndex[i]]);
|
|
}
|
|
if (Hall is null)
|
|
NUD_HallStreaks.Visible = NUD_HallStreaks.Enabled = false;
|
|
|
|
editing = true;
|
|
CB_Stats1.Items.Clear();
|
|
for (BattleFrontierFacility4 i = 0; i <= SAV.MaxFacility; i++)
|
|
CB_Stats1.Items.Add(i.ToString());
|
|
StatRBA[0].Checked = true;
|
|
|
|
// Clear Listbox and ComboBox
|
|
CB_Species.Items.Clear();
|
|
|
|
// Fill List
|
|
CB_Species.InitializeBinding();
|
|
|
|
var speciesList = GameInfo.FilteredSources.Species.Skip(1).ToList();
|
|
CB_Species.DataSource = new BindingSource(speciesList, string.Empty);
|
|
|
|
editing = false;
|
|
CB_Stats1.SelectedIndex = 0;
|
|
}
|
|
|
|
private void SaveBattleFrontier()
|
|
{
|
|
if (HallStatUpdated)
|
|
Hall?.RefreshChecksum();
|
|
}
|
|
|
|
private void SetPrintColors(ReadOnlySpan<Control> controls)
|
|
{
|
|
for (int i = 0; i < controls.Length; i++)
|
|
{
|
|
var pb = controls[i];
|
|
var workIndex = PrintIndexStart + i;
|
|
var value = SAV.GetWork(workIndex);
|
|
SetPrintColor(pb, (BattleFrontierPrintStatus4)value);
|
|
}
|
|
}
|
|
|
|
private static void SetPrintColor(Control pb, BattleFrontierPrintStatus4 value)
|
|
{
|
|
bool ready = value is BattleFrontierPrintStatus4.FirstReady or BattleFrontierPrintStatus4.SecondReady;
|
|
if (ready)
|
|
pb.ForeColor = Color.Red;
|
|
else if (value != 0)
|
|
pb.ForeColor = Color.Green;
|
|
else
|
|
pb.ResetForeColor();
|
|
|
|
if (value is BattleFrontierPrintStatus4.FirstReady or BattleFrontierPrintStatus4.FirstReceived)
|
|
pb.BackColor = Color.Silver;
|
|
else if (value is BattleFrontierPrintStatus4.SecondReady or BattleFrontierPrintStatus4.SecondReceived)
|
|
pb.BackColor = Color.Gold;
|
|
else
|
|
pb.ResetBackColor();
|
|
}
|
|
|
|
private void BTN_Print_Click(object sender, EventArgs e)
|
|
{
|
|
if (sender is not Button b)
|
|
return;
|
|
int index = PrintButtonA.IndexOf(b);
|
|
if (index < 0)
|
|
return;
|
|
index += PrintIndexStart;
|
|
var current = SAV.GetWork(index);
|
|
current++;
|
|
if (current > (int)BattleFrontierPrintStatus4.SecondReceived)
|
|
current = 0;
|
|
SAV.SetWork(index, current);
|
|
|
|
SetPrintColor(b, (BattleFrontierPrintStatus4)current);
|
|
}
|
|
|
|
private void ChangeStat1(object sender, EventArgs e)
|
|
{
|
|
if (editing)
|
|
return;
|
|
int facility = CB_Stats1.SelectedIndex;
|
|
if (facility < 0)
|
|
return;
|
|
|
|
editing = true;
|
|
CB_Stats2.Items.Clear();
|
|
CB_Stats2.Items.AddRange(BFT[BFF[facility][1]]);
|
|
|
|
StatRBA[0].Checked = true;
|
|
foreach (RadioButton rb in StatRBA)
|
|
rb.Visible = rb.Enabled = facility == 1;
|
|
|
|
for (int i = 0; i < StatLabelA.Length; i++)
|
|
StatLabelA[i].Visible = StatLabelA[i].Enabled = StatNUDA[i].Visible = StatNUDA[i].Enabled = BFV[BFF[facility][0]].Contains(i);
|
|
if (facility == 0)
|
|
{
|
|
StatLabelA[1].Visible = StatLabelA[1].Enabled = StatNUDA[1].Visible = StatNUDA[1].Enabled = true;
|
|
StatLabelA[1].Text = "Continue";
|
|
StatNUDA[1].Maximum = 65535;
|
|
}
|
|
else
|
|
{
|
|
if (StatNUDA[1].Value > 9999)
|
|
StatNUDA[1].Value = 9999;
|
|
StatNUDA[1].Maximum = 9999;
|
|
}
|
|
|
|
if (facility == 1)
|
|
StatLabelA[1].Text = StatLabelA[3].Text = "Trade";
|
|
else if (facility == 3)
|
|
StatLabelA[1].Text = StatLabelA[3].Text = "CP";
|
|
|
|
GB_Hall.Visible = facility == 2;
|
|
GB_Castle.Visible = facility == 3;
|
|
|
|
editing = false;
|
|
CB_Stats2.SelectedIndex = 0;
|
|
}
|
|
|
|
private void ChangeStat(object sender, EventArgs e)
|
|
{
|
|
if (editing)
|
|
return;
|
|
if (sender is RadioButton { Checked: false })
|
|
return;
|
|
StatAddrControl(SetValToSav: -2, SetSavToVal: true);
|
|
if (GB_Hall.Visible && CB_Stats2.SelectedItem is string sH)
|
|
{
|
|
GB_Hall.Text = $"Battle Hall ({sH})";
|
|
editing = true;
|
|
GetHallStat();
|
|
editing = false;
|
|
}
|
|
else if (GB_Castle.Visible && CB_Stats2.SelectedItem is string sC)
|
|
{
|
|
GB_Castle.Text = $"Battle Castle ({sC})";
|
|
editing = true;
|
|
GetCastleStat();
|
|
editing = false;
|
|
}
|
|
}
|
|
|
|
private void StatAddrControl(int SetValToSav = -2, bool SetSavToVal = false)
|
|
{
|
|
int Facility = CB_Stats1.SelectedIndex;
|
|
int BattleType = CB_Stats2.SelectedIndex;
|
|
int RBi = StatRBA[1].Checked ? 1 : 0;
|
|
int addrVal = BFF[Facility][2] + (BFF[Facility][3] * BattleType) + (RBi << 3);
|
|
int addrFlag = BFF[Facility][4];
|
|
byte maskFlag = (byte)(1 << (BattleType + (RBi << 2)));
|
|
int TowerContinueCountOfs = SAV is SAV4DP ? 3 : 1;
|
|
|
|
var general = SAV.General;
|
|
if (SetSavToVal)
|
|
{
|
|
editing = true;
|
|
for (int i = 0; i < BFV[BFF[Facility][0]].Length; i++)
|
|
{
|
|
if (BFV[BFF[Facility][0]][i] < 0)
|
|
continue;
|
|
int vali = ReadUInt16LittleEndian(general[(addrVal + (i << 1))..]);
|
|
StatNUDA[BFV[BFF[Facility][0]][i]].Value = vali > 9999 ? 9999 : vali;
|
|
}
|
|
CHK_Continue.Checked = (SAV.General[addrFlag] & maskFlag) != 0;
|
|
|
|
if (Facility == 0) // tower continue count
|
|
StatNUDA[1].Value = ReadUInt16LittleEndian(general[(addrFlag + TowerContinueCountOfs + (BattleType << 1))..]);
|
|
|
|
editing = false;
|
|
return;
|
|
}
|
|
if (SetValToSav >= 0)
|
|
{
|
|
ushort val = (ushort)StatNUDA[SetValToSav].Value;
|
|
|
|
if (Facility == 0 && SetValToSav == 1) // tower continue count
|
|
{
|
|
var offset = addrFlag + TowerContinueCountOfs + (BattleType << 1);
|
|
WriteUInt16LittleEndian(general[offset..], val);
|
|
}
|
|
|
|
SetValToSav = BFV[BFF[Facility][0]].IndexOf(SetValToSav);
|
|
if (SetValToSav < 0)
|
|
return;
|
|
var clamp = Math.Min((ushort)9999, val);
|
|
WriteUInt16LittleEndian(general[(addrVal + (SetValToSav << 1))..], clamp);
|
|
return;
|
|
}
|
|
if (SetValToSav == -1)
|
|
{
|
|
if (CHK_Continue.Checked)
|
|
{
|
|
general[addrFlag] |= maskFlag;
|
|
if (Facility == 3)
|
|
general[addrFlag + 1] |= 0x01; // not found what this flag means
|
|
}
|
|
else
|
|
{
|
|
general[addrFlag] &= (byte)~maskFlag;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void ChangeStatVal(object sender, EventArgs e)
|
|
{
|
|
if (editing)
|
|
return;
|
|
|
|
int n = StatNUDA.IndexOf(sender);
|
|
if (n < 0)
|
|
return;
|
|
|
|
StatAddrControl(SetValToSav: n, SetSavToVal: false);
|
|
|
|
if (CB_Stats1.SelectedIndex != 0)
|
|
return;
|
|
|
|
const int bias = 7;
|
|
var n0 = StatNUDA[0];
|
|
var n1 = StatNUDA[1];
|
|
if (Math.Floor(n0.Value / bias) == n1.Value)
|
|
return;
|
|
|
|
if (n == 0)
|
|
{
|
|
n1.Value = Math.Floor(n0.Value / bias);
|
|
}
|
|
else if (n == 1)
|
|
{
|
|
if (n0.Maximum > n1.Value * bias)
|
|
n0.Value = n1.Value * bias;
|
|
else if (n0.Value < n0.Maximum)
|
|
n0.Value = n0.Maximum;
|
|
}
|
|
}
|
|
|
|
private void CHK_Continue_CheckedChanged(object sender, EventArgs e)
|
|
{
|
|
if (editing)
|
|
return;
|
|
StatAddrControl(SetValToSav: -1, SetSavToVal: false);
|
|
}
|
|
|
|
private ushort species = ushort.MaxValue;
|
|
|
|
private void ChangeSpecies(object sender, EventArgs e)
|
|
{
|
|
species = (ushort)WinFormsUtil.GetIndex(CB_Species);
|
|
if (editing)
|
|
return;
|
|
|
|
editing = true;
|
|
GetHallStat();
|
|
editing = false;
|
|
}
|
|
|
|
private void GetCastleStat()
|
|
{
|
|
int ofs = BFF[3][2] + (BFF[3][3] * CB_Stats2.SelectedIndex) + 0x0A;
|
|
NumericUpDown[] na = [NUD_CastleRankRcv, NUD_CastleRankItem, NUD_CastleRankInfo];
|
|
for (int i = 0; i < na.Length; i++)
|
|
{
|
|
int val = ReadInt16LittleEndian(SAV.General[(ofs + (i << 1))..]);
|
|
na[i].Value = val > na[i].Maximum ? na[i].Maximum : val < na[i].Minimum ? na[i].Minimum : val;
|
|
}
|
|
}
|
|
|
|
private void NUD_CastleRank_ValueChanged(object sender, EventArgs e)
|
|
{
|
|
if (editing)
|
|
return;
|
|
NumericUpDown[] na = [NUD_CastleRankRcv, NUD_CastleRankItem, NUD_CastleRankInfo];
|
|
int i = na.IndexOf(sender);
|
|
if (i < 0)
|
|
return;
|
|
var offset = BFF[3][2] + (BFF[3][3] * CB_Stats2.SelectedIndex) + 0x0A + (i << 1);
|
|
WriteInt32LittleEndian(SAV.General[offset..], (int)na[i].Value);
|
|
}
|
|
|
|
private void GetHallStat()
|
|
{
|
|
int ofscur = BFF[2][2] + (BFF[2][3] * CB_Stats2.SelectedIndex);
|
|
var curspe = ReadUInt16LittleEndian(SAV.General[(ofscur + 4)..]);
|
|
bool c = curspe == species;
|
|
CHK_HallCurrent.Checked = c;
|
|
CHK_HallCurrent.Text = curspe > 0 && curspe <= SAV.MaxSpeciesID
|
|
? $"Current: {SpeciesName.GetSpeciesNameGeneration(curspe, GameLanguage.GetLanguageIndex(Main.CurrentLanguage), 4)}"
|
|
: "Current: (None)";
|
|
|
|
int s = 0;
|
|
for (int i = 0; i < HallNUDA.Length; i++)
|
|
{
|
|
var d = c ? Math.Min(10, (SAV.General[ofscur + 6 + ((i >> 1) << 1)] >> ((i & 1) << 2)) & 0x0F) : 0;
|
|
HallNUDA[i].Value = d;
|
|
HallNUDA[i].Enabled = c;
|
|
s += d;
|
|
}
|
|
L_SumHall.Text = s.ToString();
|
|
|
|
if (Hall is not null)
|
|
{
|
|
ushort v = Hall.GetCount(CB_Stats2.SelectedIndex, species);
|
|
NUD_HallStreaks.Value = Math.Min((ushort)9999, v);
|
|
}
|
|
}
|
|
|
|
private void CHK_HallCurrent_CheckedChanged(object sender, EventArgs e)
|
|
{
|
|
if (editing)
|
|
return;
|
|
var offset = BFF[2][2] + (BFF[2][3] * CB_Stats2.SelectedIndex) + 4;
|
|
ushort value = (ushort)(CHK_HallCurrent.Checked ? species : 0);
|
|
WriteUInt16LittleEndian(SAV.General[offset..], value);
|
|
editing = true;
|
|
GetHallStat();
|
|
editing = false;
|
|
}
|
|
|
|
private void NUD_HallType_ValueChanged(object sender, EventArgs e)
|
|
{
|
|
if (editing)
|
|
return;
|
|
int i = HallNUDA.IndexOf(sender);
|
|
if (i < 0)
|
|
return;
|
|
int ofs = BFF[2][2] + (BFF[2][3] * CB_Stats2.SelectedIndex) + 6 + ((i >> 1) << 1);
|
|
SAV.General[ofs] = (byte)((SAV.General[ofs] & ~(0xF << ((i & 1) << 2))) | ((int)HallNUDA[i].Value << ((i & 1) << 2)));
|
|
L_SumHall.Text = HallNUDA.Sum(x => x.Value).ToString(CultureInfo.InvariantCulture);
|
|
}
|
|
|
|
private void NUD_HallStreaks_ValueChanged(object sender, EventArgs e)
|
|
{
|
|
if (editing || Hall is null)
|
|
return;
|
|
Hall.SetCount(CB_Stats2.SelectedIndex, species, (ushort)NUD_HallStreaks.Value);
|
|
HallStatUpdated = true;
|
|
}
|
|
#endregion
|
|
|
|
#region Walker
|
|
private void ReadWalker(SAV4HGSS s)
|
|
{
|
|
ReadOnlySpan<string> walkercourses = GameInfo.Sources.Strings.walkercourses;
|
|
CLB_WalkerCourses.Items.Clear();
|
|
foreach (var name in walkercourses)
|
|
CLB_WalkerCourses.Items.Add(name);
|
|
|
|
ReadWalkerCourseUnlockFlags(s);
|
|
|
|
NUD_Watts.Value = s.PokewalkerWatts;
|
|
NUD_Steps.Value = s.PokewalkerSteps;
|
|
}
|
|
|
|
private void ReadWalkerCourseUnlockFlags(SAV4HGSS s)
|
|
{
|
|
Span<bool> courses = stackalloc bool[SAV4HGSS.PokewalkerCourseFlagCount];
|
|
s.GetPokewalkerCoursesUnlocked(courses);
|
|
for (int i = 0; i < CLB_WalkerCourses.Items.Count; i++)
|
|
CLB_WalkerCourses.SetItemChecked(i, courses[i]);
|
|
}
|
|
|
|
private void SaveWalker(SAV4HGSS s)
|
|
{
|
|
Span<bool> courses = stackalloc bool[SAV4HGSS.PokewalkerCourseFlagCount];
|
|
for (int i = 0; i < CLB_WalkerCourses.Items.Count; i++)
|
|
courses[i] = CLB_WalkerCourses.GetItemChecked(i);
|
|
s.SetPokewalkerCoursesUnlocked(courses);
|
|
|
|
s.PokewalkerWatts = (uint)NUD_Watts.Value;
|
|
s.PokewalkerSteps = (uint)NUD_Steps.Value;
|
|
}
|
|
|
|
private void B_AllWalkerCourses_Click(object sender, EventArgs e)
|
|
{
|
|
if (SAV is not SAV4HGSS s)
|
|
throw new Exception("Invalid SAV type");
|
|
s.PokewalkerCoursesUnlockAll();
|
|
ReadWalkerCourseUnlockFlags(s);
|
|
}
|
|
#endregion
|
|
|
|
private void ReadPokeathlon(SAV4HGSS s)
|
|
{
|
|
NUD_PokeathlonPoints.Value = s.PokeathlonPoints;
|
|
}
|
|
|
|
private void SavePokeathlon(SAV4HGSS s)
|
|
{
|
|
s.PokeathlonPoints = (uint)NUD_PokeathlonPoints.Value;
|
|
}
|
|
|
|
#region Seals
|
|
private void ReadSeals()
|
|
{
|
|
DGV_Seals.Rows.Clear();
|
|
DGV_Seals.Columns.Clear();
|
|
|
|
DataGridViewTextBoxColumn dgvSlot = new()
|
|
{
|
|
HeaderText = "Slot",
|
|
DisplayIndex = 0,
|
|
Width = 145,
|
|
ReadOnly = true,
|
|
};
|
|
DataGridViewTextBoxColumn dgvCount = new()
|
|
{
|
|
DisplayIndex = 1,
|
|
Width = 45,
|
|
DefaultCellStyle = { Alignment = DataGridViewContentAlignment.MiddleCenter },
|
|
MaxInputLength = 2, // 0-99
|
|
};
|
|
DGV_Seals.Columns.Add(dgvSlot);
|
|
DGV_Seals.Columns.Add(dgvCount);
|
|
|
|
const int count = (int)Seal4.MAX;
|
|
DGV_Seals.Rows.Add(count);
|
|
for (int i = 0; i < count; i++)
|
|
DGV_Seals.Rows[i].Cells[0].Value = seals[i];
|
|
LoadSealsCount();
|
|
}
|
|
|
|
private void LoadSealsCount()
|
|
{
|
|
for (int i = 0; i < (int)Seal4.MAX; i++)
|
|
DGV_Seals.Rows[i].Cells[1].Value = SAV.GetSealCount((Seal4)i).ToString();
|
|
}
|
|
|
|
public void ClearSeals()
|
|
{
|
|
for (int i = 0; i < (int)Seal4.MAX; i++)
|
|
DGV_Seals.Rows[i].Cells[1].Value = "0";
|
|
}
|
|
|
|
public void SetAllSeals(bool unreleased = false)
|
|
{
|
|
var sealIndexCount = (int)(unreleased ? Seal4.MAX : Seal4.MAXLEGAL);
|
|
for (int i = 0; i < sealIndexCount; i++)
|
|
DGV_Seals.Rows[i].Cells[1].Value = SAV4.SealMaxCount.ToString();
|
|
}
|
|
|
|
private void SaveSeals()
|
|
{
|
|
for (int i = 0; i < (int)Seal4.MAX; i++)
|
|
{
|
|
var cells = DGV_Seals.Rows[i].Cells;
|
|
var count = int.TryParse(cells[1].Value?.ToString() ?? "0", out var val) ? val : 0;
|
|
SAV.SetSealCount((Seal4)i, (byte)Math.Clamp(count, 0, byte.MaxValue));
|
|
}
|
|
}
|
|
|
|
private void B_ClearSeals_Click(object sender, EventArgs e) => ClearSeals();
|
|
|
|
private void OnBAllSealsLegalOnClick(object sender, EventArgs e)
|
|
{
|
|
bool setUnreleasedIndexes = sender == B_AllSealsIllegal;
|
|
SetAllSeals(setUnreleasedIndexes);
|
|
System.Media.SystemSounds.Asterisk.Play();
|
|
}
|
|
#endregion
|
|
|
|
#region Accessories
|
|
private void ReadAccessories()
|
|
{
|
|
DGV_Accessories.Rows.Clear();
|
|
DGV_Accessories.Columns.Clear();
|
|
|
|
DataGridViewTextBoxColumn dgvSlot = new()
|
|
{
|
|
HeaderText = "Slot",
|
|
DisplayIndex = 0,
|
|
Width = 140,
|
|
ReadOnly = true,
|
|
};
|
|
DataGridViewTextBoxColumn dgvCount = new()
|
|
{
|
|
DisplayIndex = 1,
|
|
Width = 50,
|
|
DefaultCellStyle = { Alignment = DataGridViewContentAlignment.MiddleCenter },
|
|
MaxInputLength = 1, // 0-9
|
|
};
|
|
DGV_Accessories.Columns.Add(dgvSlot);
|
|
DGV_Accessories.Columns.Add(dgvCount);
|
|
|
|
const int count = AccessoryInfo.Count;
|
|
DGV_Accessories.Rows.Add(count);
|
|
for (int i = 0; i < count; i++)
|
|
DGV_Accessories.Rows[i].Cells[0].Value = accessories[i];
|
|
LoadAccessoriesCount();
|
|
}
|
|
|
|
private void LoadAccessoriesCount()
|
|
{
|
|
for (int i = 0; i < AccessoryInfo.Count; i++)
|
|
DGV_Accessories.Rows[i].Cells[1].Value = SAV.GetAccessoryOwnedCount((Accessory4)i).ToString();
|
|
}
|
|
|
|
public void ClearAccessories()
|
|
{
|
|
for (int i = 0; i < AccessoryInfo.Count; i++)
|
|
DGV_Accessories.Rows[i].Cells[1].Value = "0";
|
|
}
|
|
|
|
public void SetAllAccessories(bool unreleased = false)
|
|
{
|
|
for (int i = 0; i <= AccessoryInfo.MaxMulti; i++)
|
|
DGV_Accessories.Rows[i].Cells[1].Value = AccessoryInfo.AccessoryMaxCount.ToString();
|
|
|
|
var count = unreleased ? AccessoryInfo.Count : (AccessoryInfo.MaxLegal + 1);
|
|
for (int i = AccessoryInfo.MaxMulti + 1; i < count; i++)
|
|
DGV_Accessories.Rows[i].Cells[1].Value = "1";
|
|
}
|
|
|
|
private void SaveAccessories()
|
|
{
|
|
for (int i = 0; i < AccessoryInfo.Count; i++)
|
|
{
|
|
var cells = DGV_Accessories.Rows[i].Cells;
|
|
var count = int.TryParse(cells[1].Value?.ToString() ?? "0", out var val) ? val : 0;
|
|
SAV.SetAccessoryOwnedCount((Accessory4)i, (byte)Math.Clamp(count, 0, byte.MaxValue));
|
|
}
|
|
}
|
|
|
|
private void B_ClearAccessories_Click(object sender, EventArgs e) => ClearAccessories();
|
|
|
|
private void OnBAllAccessoriesLegalOnClick(object sender, EventArgs e)
|
|
{
|
|
bool setUnreleasedIndexes = sender == B_AllAccessoriesIllegal;
|
|
SetAllAccessories(setUnreleasedIndexes);
|
|
System.Media.SystemSounds.Asterisk.Play();
|
|
}
|
|
#endregion
|
|
|
|
#region Backdrops
|
|
private void ReadBackdrops()
|
|
{
|
|
DGV_Backdrops.Rows.Clear();
|
|
DGV_Backdrops.Columns.Clear();
|
|
|
|
DataGridViewComboBoxColumn dgv = new()
|
|
{
|
|
HeaderText = "Slot",
|
|
DisplayStyle = DataGridViewComboBoxDisplayStyle.Nothing,
|
|
DisplayIndex = 0,
|
|
Width = 190,
|
|
FlatStyle = FlatStyle.Flat,
|
|
DataSource = new BindingSource(backdropsSorted, string.Empty),
|
|
};
|
|
DGV_Backdrops.Columns.Add(dgv);
|
|
|
|
LoadBackdropPositions();
|
|
}
|
|
|
|
private void LoadBackdropPositions()
|
|
{
|
|
const int count = BackdropInfo.Count;
|
|
DGV_Backdrops.Rows.Add(count);
|
|
ClearBackdrops();
|
|
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
var pos = SAV.GetBackdropPosition((Backdrop4)i);
|
|
if (pos < BackdropInfo.Count)
|
|
DGV_Backdrops.Rows[pos].Cells[0].Value = backdrops[i];
|
|
}
|
|
}
|
|
|
|
private void ClearBackdrops()
|
|
{
|
|
for (int i = 0; i < BackdropInfo.Count; i++)
|
|
DGV_Backdrops.Rows[i].Cells[0].Value = backdrops[(int)Backdrop4.Unset];
|
|
}
|
|
|
|
public void SetAllBackdrops(bool unreleased = false)
|
|
{
|
|
var count = unreleased ? BackdropInfo.Count : ((int)BackdropInfo.MaxLegal + 1);
|
|
for (int i = 0; i < count; i++)
|
|
DGV_Backdrops.Rows[i].Cells[0].Value = backdrops[i];
|
|
}
|
|
|
|
private void SaveBackdrops()
|
|
{
|
|
for (int i = 0; i < BackdropInfo.Count; i++)
|
|
SAV.RemoveBackdrop((Backdrop4)i); // clear all slots
|
|
|
|
byte ctr = 0;
|
|
for (int i = 0; i < BackdropInfo.Count; i++)
|
|
{
|
|
var bd = (Backdrop4)backdrops.IndexOf(DGV_Backdrops.Rows[i].Cells[0].Value);
|
|
if (bd.IsUnset()) // skip empty slots
|
|
continue;
|
|
|
|
SAV.SetBackdropPosition(bd, ctr);
|
|
ctr++;
|
|
}
|
|
}
|
|
|
|
private void B_ClearBackdrops_Click(object sender, EventArgs e) => ClearBackdrops();
|
|
|
|
private void OnBAllBackdropsLegalOnClick(object sender, EventArgs e)
|
|
{
|
|
bool setUnreleasedIndexes = sender == B_AllBackdropsIllegal;
|
|
SetAllBackdrops(setUnreleasedIndexes);
|
|
System.Media.SystemSounds.Asterisk.Play();
|
|
}
|
|
#endregion
|
|
|
|
#region Records
|
|
private void ReadRecord()
|
|
{
|
|
NUD_Record16.Maximum = Record4.Record16 - 1;
|
|
NUD_Record32.Maximum = Record.Record32 - 1;
|
|
NUD_Record16V.Value = Record.GetRecord16(0);
|
|
NUD_Record32V.Value = Record.GetRecord32(0);
|
|
NUD_Record16V.ValueChanged += (_, _) => Record.SetRecord16((int)NUD_Record16.Value, (ushort)NUD_Record16V.Value);
|
|
NUD_Record32V.ValueChanged += (_, _) => Record.SetRecord32((int)NUD_Record32.Value, (uint)NUD_Record32V.Value);
|
|
NUD_Record16.ValueChanged += (_, _) => NUD_Record16V.Value = Record.GetRecord16((int)NUD_Record16.Value);
|
|
NUD_Record32.ValueChanged += (_, _) => NUD_Record32V.Value = Record.GetRecord32((int)NUD_Record32.Value);
|
|
}
|
|
|
|
private void SaveRecord() => Record.EndAccess();
|
|
#endregion
|
|
}
|
|
|
|
public static class PoketchDotMatrix
|
|
{
|
|
public const int DotMatrixHeight = 20;
|
|
public const int DotMatrixWidth = 24;
|
|
public const int DotMatrixPixelCount = DotMatrixHeight * DotMatrixWidth;
|
|
|
|
public static ReadOnlySpan<byte> ColorTable => [248, 168, 88, 8];
|
|
|
|
public static bool TryBuild(string fileName, Span<byte> result)
|
|
{
|
|
if (FileUtil.GetFileSize(fileName) > 0x80A)
|
|
return false;
|
|
|
|
using var bmp = (Bitmap)Image.FromFile(fileName);
|
|
if (bmp.Width != DotMatrixWidth || bmp.Height != DotMatrixHeight)
|
|
return false;
|
|
|
|
Span<byte> brightMap = stackalloc byte[DotMatrixPixelCount];
|
|
Span<byte> brightCount = stackalloc byte[0x100];
|
|
if (!TryBuildBrightMap(bmp, brightMap, brightCount, out int colorCount))
|
|
return false;
|
|
|
|
Build(colorCount, brightCount, brightMap, result);
|
|
return true;
|
|
}
|
|
|
|
private static bool TryBuildBrightMap(Bitmap bmp, Span<byte> brightMap, Span<byte> brightCount, out int colorCount)
|
|
{
|
|
for (int iy = 0; iy < DotMatrixHeight; iy++) // Height
|
|
{
|
|
for (int ix = 0; ix < DotMatrixWidth; ix++) // Width
|
|
{
|
|
var ig = (byte)(0xFF * bmp.GetPixel(ix, iy).GetBrightness());
|
|
brightMap[ix + (DotMatrixWidth * iy)] = ig;
|
|
brightCount[ig]++;
|
|
}
|
|
}
|
|
colorCount = brightCount.Length - brightCount.Count<byte>(0);
|
|
return (colorCount - 1) <= 3; // 1-4
|
|
}
|
|
|
|
private static void Build(int colorCount, Span<byte> brightCount, Span<byte> brightMap, Span<byte> result)
|
|
{
|
|
int errorsMin = int.MaxValue;
|
|
Span<byte> LCT = stackalloc byte[4];
|
|
for (int i = 0; i < LCT.Length; i++)
|
|
LCT[i] = (byte)(colorCount < i + 1 ? 4 : colorCount - i - 1);
|
|
|
|
Span<byte> mLCT = stackalloc byte[4];
|
|
Span<byte> iBrightCount = stackalloc byte[0x100];
|
|
int ee = 0;
|
|
while (++ee < 1000)
|
|
{
|
|
brightCount.CopyTo(iBrightCount);
|
|
for (int i = 0, j = 0; i < iBrightCount.Length; i++)
|
|
{
|
|
if (iBrightCount[i] > 0)
|
|
iBrightCount[i] = LCT[j++];
|
|
}
|
|
|
|
var errorsTotal = 0;
|
|
for (int i = 0; i < DotMatrixPixelCount; i++)
|
|
errorsTotal += Math.Abs(brightMap[i] - ColorTable[iBrightCount[brightMap[i]]]);
|
|
if (errorsMin > errorsTotal)
|
|
{
|
|
errorsMin = errorsTotal;
|
|
LCT.CopyTo(mLCT);
|
|
}
|
|
GetNextLCT(LCT);
|
|
if (LCT[0] >= 4)
|
|
break;
|
|
}
|
|
for (int i = 0, j = 0; i < brightCount.Length; i++)
|
|
{
|
|
if (brightCount[i] > 0)
|
|
brightCount[i] = mLCT[j++];
|
|
}
|
|
|
|
for (int i = 0; i < brightMap.Length; i++)
|
|
brightMap[i] = brightCount[brightMap[i]];
|
|
|
|
for (int i = 0; i < brightMap.Length; i++)
|
|
result[i >> 2] |= (byte)((brightMap[i] & 3) << ((i % 4) << 1));
|
|
}
|
|
|
|
private static void GetNextLCT(Span<byte> inp)
|
|
{
|
|
while (true)
|
|
{
|
|
if (++inp[0] < 4)
|
|
continue;
|
|
|
|
inp[0] = 0;
|
|
if (++inp[1] < 4)
|
|
continue;
|
|
|
|
inp[1] = 0;
|
|
if (++inp[2] < 4)
|
|
continue;
|
|
|
|
inp[2] = 0;
|
|
if (++inp[3] < 4)
|
|
continue;
|
|
|
|
inp[0] = 4;
|
|
return;
|
|
}
|
|
}
|
|
|
|
public const int DotMatrixUpscaleFactor = 4;
|
|
|
|
public static Bitmap GetDotArt(ReadOnlySpan<byte> inp)
|
|
{
|
|
byte[] dupbyte = new byte[23040];
|
|
for (int iy = 0; iy < DotMatrixHeight; iy++)
|
|
{
|
|
for (int ix = 0; ix < DotMatrixWidth; ix++)
|
|
{
|
|
var ib = ix + (DotMatrixWidth * iy);
|
|
var ict = ColorTable[(inp[ib >> 2] >> ((ib % 4) << 1)) & 3];
|
|
var iz = (12 * ix) + (0x480 * iy);
|
|
for (int izy = 0; izy < 4; izy++)
|
|
{
|
|
for (int izx = 0; izx < 4; izx++)
|
|
{
|
|
for (int ic = 0; ic < 3; ic++)
|
|
dupbyte[ic + (3 * izx) + (0x120 * izy) + iz] = ict;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
var dabmp = new Bitmap(DotMatrixWidth * DotMatrixUpscaleFactor, DotMatrixHeight * DotMatrixUpscaleFactor);
|
|
var dabdata = dabmp.LockBits(new Rectangle(0, 0, dabmp.Width, dabmp.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
|
|
System.Runtime.InteropServices.Marshal.Copy(dupbyte, 0, dabdata.Scan0, dupbyte.Length);
|
|
dabmp.UnlockBits(dabdata);
|
|
return dabmp;
|
|
}
|
|
}
|