-Controller type for GBA.

-Cleanup (adelikat inspired me to always use braces).
This commit is contained in:
brandman211 2012-12-06 05:19:08 +00:00
parent e449ab34fd
commit a346389c2f
2 changed files with 218 additions and 33 deletions

View File

@ -62,7 +62,7 @@ namespace BizHawk.MultiClient
"GBA Controller", new Dictionary<string, string>()
{
{"Up", "U"}, {"Down", "D"}, {"Left", "L"}, {"Right", "R"}, {"Select", "s"}, {"Start", "S"}, {"B", "B"},
{"A", "A"}, {"L", "L"}, {"R", "R"},
{"A", "A"}, {"L", "L"}, {"R", "R"}
}
},
{
@ -83,7 +83,7 @@ namespace BizHawk.MultiClient
"SNES Controller", new Dictionary<string, string>()
{
{"Up", "U"}, {"Down", "D"}, {"Left", "L"}, {"Right", "R"}, {"Select", "s"}, {"Start", "S"}, {"B", "B"},
{"A", "A"}, {"X", "X"}, {"Y", "Y"}, {"L", "L"}, {"R", "R"},
{"A", "A"}, {"X", "X"}, {"Y", "Y"}, {"L", "L"}, {"R", "R"}
}
},
{
@ -160,9 +160,8 @@ namespace BizHawk.MultiClient
public static readonly Dictionary<string, int> PLAYERS = new Dictionary<string, int>()
{
{"Gameboy Controller", 1}, {"Genesis 3-Button Controller", 2}, {"NES Controller", 4},
{"SNES Controller", 4},
{"PC Engine Controller", 5}, {"SMS Controller", 2}, {"TI83 Controller", 1}, {"Atari 2600 Basic Controller", 2},
{"Gameboy Controller", 1}, {"GBA Controller", 1}, {"Genesis 3-Button Controller", 2}, {"NES Controller", 4},
{"SNES Controller", 4}, {"PC Engine Controller", 5}, {"SMS Controller", 2}, {"TI83 Controller", 1}, {"Atari 2600 Basic Controller", 2},
{"ColecoVision Basic Controller", 2}, {"Commodore 64 Controller", 2}
};

View File

@ -100,8 +100,12 @@ namespace BizHawk.MultiClient
"FCM", "FM2", "FMV", "GMV", "MCM", "MC2", "MMV", "NMV", "LSMV", "SMV", "VBM", "VMV", "ZMV"
};
foreach (string ext in extensions)
{
if (extension.ToUpper() == "." + ext)
{
return true;
}
}
return false;
}
@ -170,7 +174,9 @@ namespace BizHawk.MultiClient
break;
case '2':
if (m.Frames != 0)
{
warningMsg = "hard reset";
}
break;
case '4':
warningMsg = "FDS Insert";
@ -183,9 +189,11 @@ namespace BizHawk.MultiClient
break;
}
if (warningMsg != "")
{
warningMsg = "Unable to import " + warningMsg + " command on line " + lineNum + ".";
}
}
}
if (Path.GetExtension(path).ToUpper() == ".LSMV" && sections.Length != 0)
{
string flags = sections[0];
@ -201,7 +209,9 @@ namespace BizHawk.MultiClient
if (reset && ((flags.Length >= 2 && flags[1] != '0') || (flags.Length >= 4 && flags[3] != '0')))
{
if (warningMsg == "")
{
warningMsg = "Unable to import delayed reset.";
}
return m;
}
controllers["Reset"] = reset;
@ -227,16 +237,22 @@ namespace BizHawk.MultiClient
string prefix = "P" + (player).ToString() + " ";
// Gameboy doesn't currently have a prefix saying which player the input is for.
if (controllers.Type.Name == "Gameboy Controller")
{
prefix = "";
}
// Only count lines with that have the right number of buttons and are for valid players.
if (
sections[section].Length == buttons.Length &&
player <= Global.PLAYERS[controllers.Type.Name]
)
{
for (int button = 0; button < buttons.Length; button++)
{
// Consider the button pressed so long as its spot is not occupied by a ".".
controllers[prefix + buttons[button]] = (sections[section][button] != '.');
}
}
}
// Convert the data for the controllers to a mnemonic and add it as a frame.
mg.SetSource(controllers);
m.AppendFrame(mg.GetControllersAsMnemonic());
@ -300,7 +316,9 @@ namespace BizHawk.MultiClient
{
lineNum++;
if (line == "")
{
continue;
}
else if (line[0] == '|')
{
m = ImportTextFrame(line, lineNum, m, path, platform, ref warningMsg, ref errorMsg);
@ -311,11 +329,15 @@ namespace BizHawk.MultiClient
}
}
else if (line.ToLower().StartsWith("sub"))
{
m = ImportTextSubtitle(line, m, path);
}
else if (line.ToLower().StartsWith("emuversion"))
{
m.Header.Comments.Add(
EMULATIONORIGIN + " " + emulator + " version " + ParseHeader(line, "emuVersion")
);
}
else if (line.ToLower().StartsWith("version"))
{
string version = ParseHeader(line, "version");
@ -330,18 +352,26 @@ namespace BizHawk.MultiClient
}
}
else if (line.ToLower().StartsWith("romfilename"))
{
m.Header.SetHeaderLine(MovieHeader.GAMENAME, ParseHeader(line, "romFilename"));
}
else if (line.ToLower().StartsWith("romchecksum"))
{
string blob = ParseHeader(line, "romChecksum");
byte[] md5 = DecodeBlob(blob);
if (md5 != null && md5.Length == 16)
{
m.Header.SetHeaderLine(MD5, BizHawk.Util.BytesToHexString(md5).ToLower());
}
else
{
warningMsg = "Bad ROM checksum.";
}
}
else if (line.ToLower().StartsWith("comment author"))
{
m.Header.SetHeaderLine(MovieHeader.AUTHOR, ParseHeader(line, "comment author"));
}
else if (line.ToLower().StartsWith("rerecordcount"))
{
int rerecordCount;
@ -357,7 +387,9 @@ namespace BizHawk.MultiClient
m.Rerecords = rerecordCount;
}
else if (line.ToLower().StartsWith("guid"))
{
m.Header.SetHeaderLine(MovieHeader.GUID, ParseHeader(line, "guid"));
}
else if (line.ToLower().StartsWith("startsfromsavestate"))
{
// If this movie starts from a savestate, we can't support it.
@ -402,14 +434,21 @@ namespace BizHawk.MultiClient
private static byte[] DecodeBlob(string blob)
{
if (blob.Length < 2)
{
return null;
}
if (blob[0] == '0' && (blob[1] == 'x' || blob[1] == 'X'))
{
// hex
return BizHawk.Util.HexStringToBytes(blob.Substring(2));
else {
}
else
{
// base64
if(!blob.ToLower().StartsWith("base64:"))
if (!blob.ToLower().StartsWith("base64:"))
{
return null;
}
try
{
return Convert.FromBase64String(blob.Substring(7));
@ -426,7 +465,9 @@ namespace BizHawk.MultiClient
{
int pos = str.IndexOf('\0');
if (pos != -1)
{
str = str.Substring(0, pos);
}
return str;
}
@ -561,7 +602,9 @@ namespace BizHawk.MultiClient
65536-(2^24-1) 3
*/
for (int b = 0; b < delta; b++)
{
frames += r.ReadByte() * (int)Math.Pow(2, b * 8);
}
frame += frames;
while (frames > 0)
{
@ -595,7 +638,9 @@ namespace BizHawk.MultiClient
case 2:
reset = true;
if (frame != 1)
{
warningMsg = "hard reset";
}
break;
// VS System Insert Coin
case 7:
@ -625,8 +670,10 @@ namespace BizHawk.MultiClient
break;
}
if (warningMsg != "")
{
warningMsg = "Unable to import " + warningMsg + " command at frame " + frame + ".";
}
}
/*
1 Even if the header says "movie begins from reset", the file still contains a quicksave, and the
quicksave is actually loaded. This flag can't therefore be trusted. To check if the movie actually
@ -649,7 +696,9 @@ namespace BizHawk.MultiClient
*/
int player = ((update >> 3) & 0x3) + 1;
if (player > 2)
{
fourscore = true;
}
/*
ccc:
* 0 A
@ -786,8 +835,12 @@ namespace BizHawk.MultiClient
*/
int bytesPerFrame = 0;
for (int player = 1; player <= masks.Length; player++)
{
if (masks[player - 1])
{
bytesPerFrame++;
}
}
long frameCount = (fs.Length - 144) / bytesPerFrame;
for (long frame = 1; frame <= frameCount; frame++)
{
@ -799,14 +852,22 @@ namespace BizHawk.MultiClient
for (int player = 1; player <= masks.Length; player++)
{
if (!masks[player - 1])
{
continue;
}
byte controllerState = r.ReadByte();
if (player != 3)
{
for (int button = 0; button < buttons.Length; button++)
{
controllers["P" + player + " " + buttons[button]] = (((controllerState >> button) & 0x1) != 0);
}
}
else
{
warningMsg = "FDS commands are not properly supported.";
}
}
mg.SetSource(controllers);
m.AppendFrame(mg.GetControllersAsMnemonic());
}
@ -845,7 +906,9 @@ namespace BizHawk.MultiClient
// 015 ASCII-encoded controller config for player 2. '3' or '6'.
string player2Config = r.ReadStringFixedAscii(1);
if (player1Config == "6" || player2Config == "6")
{
warningMsg = "6 button controllers are not properly supported.";
}
// 016 special flags (Version A and up only)
byte flags = r.ReadByte();
/*
@ -911,17 +974,27 @@ namespace BizHawk.MultiClient
byte controllerState = r.ReadByte();
// * is controller 3 if a 3-player movie, or XYZ-mode if a 2-player movie.
if (player != 3 || threePlayers)
{
for (int button = 0; button < buttons.Length; button++)
{
controllers["P" + player + " " + buttons[button]] = (((controllerState >> button) & 0x1) == 0);
}
}
else
{
for (int button = 0; button < other.Length; button++)
{
if (player1Config == "6")
{
controllers["P1 " + other[button]] = (((controllerState >> button) & 0x1) == 0);
}
if (player2Config == "6")
{
controllers["P2 " + other[button]] = (((controllerState >> (button + 4)) & 0x1) == 0);
}
}
}
}
mg.SetSource(controllers);
m.AppendFrame(mg.GetControllersAsMnemonic());
}
@ -961,15 +1034,21 @@ namespace BizHawk.MultiClient
if (author != "")
{
if (author_last != "")
{
author_list += author_last + ", ";
}
author_last = author;
}
}
}
if (author_list != "")
{
author_list += "and ";
}
if (author_last != "")
{
author_list += author_last;
}
m.Header.SetHeaderLine(MovieHeader.AUTHOR, author_list);
hf.Unbind();
}
@ -1027,7 +1106,9 @@ namespace BizHawk.MultiClient
while ((line = reader.ReadLine()) != null)
{
if (line == "")
{
continue;
}
m = ImportTextFrame(line, lineNum, m, path, platform, ref warningMsg, ref errorMsg);
if (errorMsg != "")
{
@ -1250,15 +1331,21 @@ namespace BizHawk.MultiClient
for (int player = 1; player <= ports; player++)
{
if (bytesPerPort == 2)
{
// Discard the first byte.
r.ReadByte();
}
ushort controllerState = r.ReadByte();
for (int button = 0; button < buttons.Length; button++)
{
controllers["P" + player + " " + buttons[button]] = (((controllerState >> button) & 0x1) != 0);
}
}
r.ReadByte();
if (platform == "nes" && warningMsg == "")
{
warningMsg = "Control commands are not properly supported.";
}
mg.SetSource(controllers);
m.AppendFrame(mg.GetControllersAsMnemonic());
}
@ -1376,13 +1463,17 @@ namespace BizHawk.MultiClient
{
byte controllerState = r.ReadByte();
for (int button = 0; button < buttons.Length; button++)
{
controllers["P" + player + " " + buttons[button]] = (((controllerState >> button) & 0x1) != 0);
}
if (player == 1)
{
controllers["Pause"] = (
(((controllerState >> 6) & 0x1) != 0 && (!gamegear)) ||
(((controllerState >> 7) & 0x1) != 0 && gamegear)
);
}
}
mg.SetSource(controllers);
m.AppendFrame(mg.GetControllersAsMnemonic());
}
@ -1469,7 +1560,9 @@ namespace BizHawk.MultiClient
per frame. Nintendulator's Four-Score recording is seemingly broken.
*/
for (int controller = 1; controller < masks.Length; controller++)
{
masks[controller - 1] = (((controller2 >> (controller - 1)) & 0x1) != 0);
}
warningMsg = "Nintendulator's Four Score recording is seemingly broken.";
}
else
@ -1505,10 +1598,12 @@ namespace BizHawk.MultiClient
break;
}
if (warningMsg != "")
{
warningMsg = warningMsg + " is not properly supported.";
}
}
}
}
// 002 1-byte expansion port controller type
byte expansion = r.ReadByte();
/*
@ -1527,14 +1622,20 @@ namespace BizHawk.MultiClient
"Alternate keyboard layout", "Family Trainer", "Oeka Kids writing tablet"
};
if (expansion != 0 && warningMsg == "")
{
warningMsg = "Expansion port is not properly supported. This movie uses " + expansions[expansion] + ".";
}
// 003 1-byte number of bytes per frame, plus flags
byte data = r.ReadByte();
int bytesPerFrame = data & 0xF;
int bytes = 0;
for (int controller = 1; controller < masks.Length; controller++)
{
if (masks[controller - 1])
{
bytes++;
}
}
/*
Depending on the mapper used by the game in question, an additional byte of data may be stored during each
frame. This is most frequently used for FDS games (storing either the disk number or 0xFF to eject) or VS
@ -1542,7 +1643,9 @@ namespace BizHawk.MultiClient
not match up with the amount of bytes the controllers take up.
*/
if (bytes != bytesPerFrame)
{
masks[4] = true;
}
// bit 6: Game Genie active
/*
bit 7: Framerate
@ -1588,14 +1691,22 @@ namespace BizHawk.MultiClient
for (int player = 1; player <= masks.Length; player++)
{
if (!masks[player - 1])
{
continue;
}
byte controllerState = r.ReadByte();
if (player != 5)
{
for (int button = 0; button < buttons.Length; button++)
{
controllers["P" + player + " " + buttons[button]] = (((controllerState >> button) & 0x1) != 0);
}
}
else if (warningMsg == "")
{
warningMsg = "Extra input is not properly supported.";
}
}
mg.SetSource(controllers);
m.AppendFrame(mg.GetControllersAsMnemonic());
}
@ -1669,7 +1780,9 @@ namespace BizHawk.MultiClient
MnemonicsGenerator mg = new MnemonicsGenerator();
bool[] controllersUsed = new bool[5];
for (int controller = 1; controller <= controllersUsed.Length; controller++)
{
controllersUsed[controller - 1] = (((controllerFlags >> (controller - 1)) & 0x1) != 0);
}
// 015 1-byte flags "movie options"
byte movieFlags = r.ReadByte();
/*
@ -1745,7 +1858,9 @@ namespace BizHawk.MultiClient
byte[] metadata = r.ReadBytes((int)(savestateOffset - extraRomInfo - ((version != "1.43") ? 0x40 : 0x20)));
string author = NullTerminated(Encoding.Unicode.GetString(metadata).Trim());
if (author != "")
{
m.Header.SetHeaderLine(MovieHeader.AUTHOR, author);
}
if (extraRomInfo == 30)
{
// 000 3 bytes of zero padding: 00 00 00 003 4-byte integer: CRC32 of the ROM 007 23-byte ascii string
@ -1784,7 +1899,9 @@ namespace BizHawk.MultiClient
for (int player = 1; player <= controllersUsed.Length; player++)
{
if (!controllersUsed[player - 1])
{
continue;
}
/*
Each frame consists of 2 bytes per controller. So if there are 3 controllers, a frame is 6 bytes and
if there is only 1 controller, a frame is 2 bytes.
@ -1796,7 +1913,9 @@ namespace BizHawk.MultiClient
reset. The reset is done through the S9xSoftReset routine.
*/
if (controllerState1 != 0xFF || controllerState2 != 0xFF)
{
controllers["Reset"] = false;
}
/*
While the meaning of controller data (for 1.51 and up) for a single standard SNES controller pad
remains the same, each frame of controller data can contain additional bytes if input for peripherals
@ -1837,22 +1956,30 @@ namespace BizHawk.MultiClient
break;
}
if (peripheral != "" && warningMsg == "")
{
warningMsg = "Unable to import " + peripheral + ".";
}
}
ushort controllerState = (ushort)(((controllerState1 << 4) & 0x0F00) | controllerState2);
if (player <= Global.PLAYERS[controllers.Type.Name])
{
for (int button = 0; button < buttons.Length; button++)
{
controllers["P" + player + " " + buttons[button]] = (
((controllerState >> button) & 0x1) != 0
);
}
}
else if (warningMsg == "")
{
warningMsg = "Controller " + player + " not supported.";
}
}
// The controller data contains <number_of_frames + 1> frames.
if (frame == 0)
{
continue;
}
mg.SetSource(controllers);
m.AppendFrame(mg.GetControllersAsMnemonic());
}
@ -1937,7 +2064,9 @@ namespace BizHawk.MultiClient
*/
bool[] controllersUsed = new bool[4];
for (int controller = 1; controller <= controllersUsed.Length; controller++)
{
controllersUsed[controller - 1] = (((controllerFlags >> (controller - 1)) & 0x1) != 0);
}
if (!controllersUsed[0])
{
errorMsg = "Controller 1 must be in use.";
@ -1966,11 +2095,17 @@ namespace BizHawk.MultiClient
// (If all 3 of these bits are "0", it is for regular GB.)
string platform = "GB";
if (is_gba)
{
platform = "GBA";
}
if (is_gbc)
{
platform = "GBC";
}
if (is_sgb)
{
m.Header.Comments.Add(SUPERGAMEBOYMODE + " True");
}
m.Header.SetHeaderLine(MovieHeader.PLATFORM, platform);
// 017 1-byte flags: (values of some boolean emulator options)
flags = r.ReadByte();
@ -2048,7 +2183,14 @@ namespace BizHawk.MultiClient
r.BaseStream.Position = firstFrameOffset;
SimpleController controllers = new SimpleController();
controllers.Type = new ControllerDefinition();
if (platform == "GBA")
{
controllers.Type.Name = "Gameboy Controller";
}
else
{
controllers.Type.Name = "GBA Controller";
}
MnemonicsGenerator mg = new MnemonicsGenerator();
/*
* 01 00 A
@ -2083,23 +2225,33 @@ namespace BizHawk.MultiClient
*/
ushort controllerState = r.ReadUInt16();
for (int button = 0; button < buttons.Length; button++)
{
controllers[buttons[button]] = (((controllerState >> button) & 0x1) != 0);
}
// TODO: Handle the other buttons.
if (warningMsg == "")
{
for (int button = 0; button < other.Length; button++)
{
if (((controllerState >> (button + 8)) & 0x1) != 0)
{
warningMsg = other[button];
break;
}
}
if (warningMsg != "")
{
warningMsg = "Unable to import " + warningMsg + " at frame " + frame + ".";
}
}
// TODO: Handle the additional controllers.
for (int player = 2; player <= controllersUsed.Length; player++)
{
if (controllersUsed[player - 1])
{
r.ReadBytes(2);
}
}
mg.SetSource(controllers);
m.AppendFrame(mg.GetControllersAsMnemonic());
}
@ -2143,7 +2295,9 @@ namespace BizHawk.MultiClient
*/
bool[] controllersUsed = new bool[4];
for (int controller = 1; controller <= controllersUsed.Length; controller++)
{
controllersUsed[controller - 1] = (((flags >> (controller - 1)) & 0x1) != 0);
}
bool fourscore = (controllersUsed[2] || controllersUsed[3]);
m.Header.SetHeaderLine(MovieHeader.FOURSCORE, fourscore.ToString());
/*
@ -2234,7 +2388,9 @@ namespace BizHawk.MultiClient
for (int player = 1; player <= controllersUsed.Length; player++)
{
if (!controllersUsed[player - 1])
{
continue;
}
byte controllerState = r.ReadByte();
if (controllerState >= 0xF0)
{
@ -2243,6 +2399,7 @@ namespace BizHawk.MultiClient
ushort command = r.ReadUInt16();
string commandName = "";
if ((command & 0xFF00) == 0)
{
switch (command & 0x00FF)
{
// NESCMD_NONE
@ -2292,23 +2449,32 @@ namespace BizHawk.MultiClient
commandName = "invalid command";
break;
}
}
else
{
commandName = "NESCMD_EXCONTROLLER, " + (command & 0xFF00);
}
if (commandName != "" && warningMsg == "")
{
warningMsg = "Unable to run command \"" + commandName + "\".";
}
}
else if (controllerState == 0xF3)
{
uint dwdata = r.ReadUInt32();
// TODO: Make a clearer warning message.
if (warningMsg == "")
{
warningMsg = "Unable to run SetSyncExData(" + dwdata + ").";
}
}
controllerState = r.ReadByte();
}
for (int button = 0; button < buttons.Length; button++)
{
controllers["P" + player + " " + buttons[button]] = (((controllerState >> button) & 0x1) != 0);
}
}
mg.SetSource(controllers);
m.AppendFrame(mg.GetControllersAsMnemonic());
}
@ -2378,18 +2544,26 @@ namespace BizHawk.MultiClient
string peripheral = "";
bool superScope = ((controllerFlags & 0x1) != 0);
if (superScope)
{
peripheral = "Super Scope";
}
controllerFlags >>= 1;
bool secondMouse = ((controllerFlags & 0x1) != 0);
if (secondMouse && peripheral == "")
{
peripheral = "Second Mouse";
}
controllerFlags >>= 1;
bool firstMouse = ((controllerFlags & 0x1) != 0);
if (firstMouse && peripheral == "")
{
peripheral = "First Mouse";
}
controllerFlags >>= 1;
if (peripheral != "")
{
warningMsg = "Unable to import " + peripheral + ".";
}
// 027 1-byte flags:
byte movieFlags = r.ReadByte();
byte begins = (byte)(movieFlags & 0xC0);
@ -2483,11 +2657,15 @@ namespace BizHawk.MultiClient
// If the event is RLE data, next follows 4 bytes which is the frame to repeat current input till.
uint frame = r.ReadUInt32();
if (frame > frameCount)
{
throw new ArgumentException("RLE data repeats for frames beyond the total frame count.");
}
mg.SetSource(controllers);
for (; frames <= frame; frames++)
{
m.AppendFrame(mg.GetControllersAsMnemonic());
}
}
else if (((flag >> 2) & 0x1) != 0)
{
/*
@ -2516,6 +2694,7 @@ namespace BizHawk.MultiClient
bool leftOver = false;
byte leftOverValue = 0x0;
for (int player = 1; player <= 5; player++)
{
// If the controller has changed:
if (((flag >> (5 - player)) & 0x1) != 0)
{
@ -2581,14 +2760,21 @@ namespace BizHawk.MultiClient
if (player <= Global.PLAYERS[controllers.Type.Name])
{
if (player != 2 || !superScope)
{
for (int button = 0; button < buttons.Length; button++)
{
controllers["P" + player + " " + buttons[button]] = (
((controllerState >> button) & 0x1) != 0
);
}
}
}
else if (warningMsg == "")
{
warningMsg = "Controller " + player + " not supported.";
}
}
}
mg.SetSource(controllers);
m.AppendFrame(mg.GetControllersAsMnemonic());
frames++;