Merge branch 'master' into give_improvements

This commit is contained in:
Lorenzooone 2020-10-29 01:07:54 +01:00 committed by GitHub
commit 39771d6bd5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 844 additions and 29 deletions

View File

@ -5,3 +5,5 @@ $pwd = $pwd.Path
if ($LASTEXITCODE -ne 0) { exit -1 }
& dotnet build tools/RenderCastRoll -o "$([IO.Path]::Combine($pwd, "bin/RenderCastRoll"))"
if ($LASTEXITCODE -ne 0) { exit -1 }
& dotnet build tools/RenderStaffCredits -o "$([IO.Path]::Combine($pwd, "bin/RenderStaffCredits"))"
if ($LASTEXITCODE -ne 0) { exit -1 }

View File

@ -1,18 +1,19 @@
[Environment]::CurrentDirectory = (Get-Location -PSProvider FileSystem).ProviderPath
#Region Variables
$input_rom_file = "bin/m12fresh.gba"
$output_rom_file = "bin/m12.gba"
$eb_rom_file = "bin/eb.smc"
$working_dir = "working"
$give_dir = "working/m12-give-strings"
$src_dir = "src"
$data_dir = "src/data"
$give_new_dir = "src/m12-give-strings"
$cast_roll_file = "working/cast_roll.json"
$compiled_asm_file = "src/m2-compiled.asm"
$includes_asm_file = "m12-includes.asm" # implicitly rooted in working_dir
$hack_asm_file = "m2-hack.asm" # implicitly rooted in src_dir
$input_rom_file = "bin/m12fresh.gba"
$output_rom_file = "bin/m12.gba"
$eb_rom_file = "bin/eb.smc"
$working_dir = "working"
$give_dir = "working/m12-give-strings"
$src_dir = "src"
$data_dir = "src/data"
$give_new_dir = "src/m12-give-strings"
$cast_roll_file = "working/cast_roll.json"
$staff_credits_file = "working/staff_text.md"
$compiled_asm_file = "src/m2-compiled.asm"
$includes_asm_file = "m12-includes.asm" # implicitly rooted in working_dir
$hack_asm_file = "m2-hack.asm" # implicitly rooted in src_dir
$input_c_files =
"src/c/ext.c",
@ -27,18 +28,19 @@ $input_c_files =
"src/c/psi.c",
"src/c/luminehall.c"
$base_c_address = 0x83755B8;
$scripttool_cmd = "bin/ScriptTool/ScriptTool.dll"
$rendercastroll_cmd= "bin/RenderCastRoll/RenderCastRoll.dll"
$gcc_cmd = "arm-none-eabi-gcc"
$ld_cmd = "arm-none-eabi-ld"
$objdump_cmd = "arm-none-eabi-objdump"
$readelf_cmd = "arm-none-eabi-readelf"
$combined_obj_file = "src/c/combined.o"
$linked_obj_file = "src/c/linked.o"
$combine_script = "src/c/combine.ld"
$link_script = "src/c/link.ld"
$undefine_obj_file = "src/c/ext.o"
$base_c_address = 0x83755B8;
$scripttool_cmd = "bin/ScriptTool/ScriptTool.dll"
$rendercastroll_cmd = "bin/RenderCastRoll/RenderCastRoll.dll"
$renderstaffcredits_cmd = "bin/RenderStaffCredits/RenderStaffCredits.dll"
$gcc_cmd = "arm-none-eabi-gcc"
$ld_cmd = "arm-none-eabi-ld"
$objdump_cmd = "arm-none-eabi-objdump"
$readelf_cmd = "arm-none-eabi-readelf"
$combined_obj_file = "src/c/combined.o"
$linked_obj_file = "src/c/linked.o"
$combine_script = "src/c/combine.ld"
$link_script = "src/c/link.ld"
$undefine_obj_file = "src/c/ext.o"
If ($IsWindows) { $asm_cmd = "bin/armips.exe" }
ElseIf ($IsLinux -or $IsMacOS) { $asm_cmd = "bin/armips" }
@ -59,6 +61,10 @@ $rendercastroll_args =
$cast_roll_file,
$data_dir
$renderstaffcredits_args =
$staff_credits_file,
$data_dir
$gcc_args =
"-c",
"-O1",
@ -365,6 +371,10 @@ Copy-Item -Path $give_dir -Destination $give_new_dir -Recurse
& dotnet $rendercastroll_cmd $rendercastroll_args
if ($LASTEXITCODE -ne 0) { exit -1 }
"Pre-rendering staff credits..."
& dotnet $renderstaffcredits_cmd $renderstaffcredits_args
if ($LASTEXITCODE -ne 0) { exit -1 }
# ------------------------ ASSEMBLE GAME TEXT -----------------------
"Assembling game text..."
& $asm_cmd -root $working_dir -sym $includes_sym_file $includes_asm_file

View File

@ -5,18 +5,20 @@ void printPlayerNameCredits(unsigned short *arrangements)
//Converts the player name to arrangements
int length = 0;
byte *player_name = m2_player1;
unsigned short yPosition = m2_credits_extras[0];
unsigned short defaultNameLength = m2_credits_extras[1];
//First things first, it calculates the length of the string
for(length = 0; length < PLAYER_NAME_SIZE && (*(++player_name)) != 0xFF; length++);
//Gets where to position the arrangements...
int start_pos = ((0x1F - length) >> 1) + 1;
int start_pos_default = ((0x1F - 5) >> 1) + 1;
unsigned short *player_name_arrangements = ((0x89 << 2) << 5) + arrangements + start_pos;
unsigned short *default_player_name_arrangements = ((0x89 << 2) << 5) + arrangements + start_pos_default;
int start_pos_default = ((0x1F - defaultNameLength) >> 1) + 1;
unsigned short *player_name_arrangements = (yPosition << 5) + arrangements + start_pos;
unsigned short *default_player_name_arrangements = (yPosition << 5) + arrangements + start_pos_default;
player_name = m2_player1;
//Clears the default MARIO player name...
for(int i = 0; i < 5; i++)
for(int i = 0; i < defaultNameLength; i++)
{
default_player_name_arrangements[i] = 0xF19B;
default_player_name_arrangements[i + 0x20] = 0xF19B;

View File

@ -35,6 +35,7 @@ extern byte m2_player1[];
extern byte cast_vwf_names[];
extern unsigned short m2_cast_vwf_free;
extern unsigned short m2_credits_conversion_table[];
extern unsigned short m2_credits_extras[];
extern int m2_bits_to_nybbles_fast_cast[];
#endif

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1742,6 +1742,10 @@ nop
//Repoint credits font (Before it pointed to 0x82FF1B8)
.org 0x82DB284 :: dw m2_credits_font
.org 0x82DB28C :: dw m2_credits_arrangements
.org 0x82DB2A4 :: .incbin "data/m2-credits-size.bin"
.org 0x80B53BC :: .incbin "data/m2-credits-scroll-size-limit.bin"
.org 0x80B53C0 :: .incbin "data/m2-credits-scroll-size.bin"
.org 0x801352E :: bl printPlayerNameCredits
//Repoint cast graphical data
@ -2015,10 +2019,19 @@ m2_cast_arrangements:
m2_credits_font:
.incbin "data/m2-credits-font_[c].bin"
.align 4
m2_credits_arrangements:
.incbin "data/m2-credits-arrangements_[c].bin"
.align 2
m2_credits_extras:
.incbin "data/m2-credits-extra-data.bin"
.align 4
m2_end_frame1:
.incbin "data/the_end_arrangements_frame1.bin"
.align 4
optimized_byte_4bpp_to_1bpp_table:
.incbin "data/optimized-byte-4bpp-to-1bpp-table.bin"

View File

@ -7,7 +7,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ScriptTool", "ScriptTool\Sc
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ScriptToolGui", "ScriptToolGui\ScriptToolGui.csproj", "{BAE5CBBF-D0B3-4F82-A80A-957CEC379238}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RenderCastRoll", "RenderCastRoll\RenderCastRoll.csproj", "{DD1C607C-5A74-4921-81A4-6BF530A191D7}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RenderCastRoll", "RenderCastRoll\RenderCastRoll.csproj", "{DD1C607C-5A74-4921-81A4-6BF530A191D7}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RenderStaffCredits", "RenderStaffCredits\RenderStaffCredits.csproj", "{D4885175-8BD3-4B91-B905-6BA845DBDC2F}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -27,6 +29,10 @@ Global
{DD1C607C-5A74-4921-81A4-6BF530A191D7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DD1C607C-5A74-4921-81A4-6BF530A191D7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DD1C607C-5A74-4921-81A4-6BF530A191D7}.Release|Any CPU.Build.0 = Release|Any CPU
{D4885175-8BD3-4B91-B905-6BA845DBDC2F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D4885175-8BD3-4B91-B905-6BA845DBDC2F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D4885175-8BD3-4B91-B905-6BA845DBDC2F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D4885175-8BD3-4B91-B905-6BA845DBDC2F}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@ -0,0 +1,30 @@
using System;
using System.IO;
namespace RenderStaffCredits
{
public static class Asset
{
public static readonly string AssetPath;
static Asset()
{
AssetPath = AppDomain.CurrentDomain.BaseDirectory;
}
public static string GetFullPath(string path)
=> Path.Combine(AssetPath, path);
public static string ReadAllText(string path)
=> File.ReadAllText(GetFullPath(path));
public static byte[] ReadAllBytes(string path)
=> File.ReadAllBytes(GetFullPath(path));
public static string[] ReadAllLines(string path)
=> File.ReadAllLines(GetFullPath(path));
public static void WriteAllText(string path, string text)
=> File.WriteAllText(GetFullPath(path), text);
}
}

View File

@ -0,0 +1,167 @@
using System.Collections.Generic;
namespace GBA
{
class LZ77
{
public static int Decompress(byte[] data, int address, out byte[] output)
{
output = null;
int start = address;
if (data[address++] != 0x10) return -1; // Check for LZ77 signature
// Read the block length
int length = data[address++];
length += (data[address++] << 8);
length += (data[address++] << 16);
output = new byte[length];
int bPos = 0;
while (bPos < length)
{
byte ch = data[address++];
for (int i = 0; i < 8; i++)
{
switch ((ch >> (7 - i)) & 1)
{
case 0:
// Direct copy
if (bPos >= length) break;
output[bPos++] = data[address++];
break;
case 1:
// Compression magic
int t = (data[address++] << 8);
t += data[address++];
int n = ((t >> 12) & 0xF) + 3; // Number of bytes to copy
int o = (t & 0xFFF);
// Copy n bytes from bPos-o to the output
for (int j = 0; j < n; j++)
{
if (bPos >= length) break;
output[bPos] = output[bPos - o - 1];
bPos++;
}
break;
default:
break;
}
}
}
return address - start;
}
public static byte[] Compress(byte[] data)
{
return Compress(data, 0, data.Length);
}
public static byte[] Compress(byte[] data, int address, int length)
{
int start = address;
List<byte> obuf = new List<byte>();
List<byte> tbuf = new List<byte>();
int control = 0;
// Let's start by encoding the signature and the length
obuf.Add(0x10);
obuf.Add((byte)(length & 0xFF));
obuf.Add((byte)((length >> 8) & 0xFF));
obuf.Add((byte)((length >> 16) & 0xFF));
while ((address - start) < length)
{
tbuf.Clear();
control = 0;
for (int i = 0; i < 8; i++)
{
bool found = false;
// First byte should be raw
if (address == start)
{
tbuf.Add(data[address++]);
found = true;
}
else if ((address - start) >= length)
{
break;
}
else
{
// We're looking for the longest possible string
// The farthest possible distance from the current address is 0x1000
int max_length = -1;
int max_distance = -1;
for (int k = 1; k <= 0x1000; k++)
{
if ((address - k) < start) break;
int l = 0;
for (; l < 18; l++)
{
if (((address - start + l) >= length) ||
(data[address - k + l] != data[address + l]))
{
if (l > max_length)
{
max_length = l;
max_distance = k;
}
break;
}
}
// Corner case: we matched all 18 bytes. This is
// the maximum length, so don't bother continuing
if (l == 18)
{
max_length = 18;
max_distance = k;
break;
}
}
if (max_length >= 3)
{
address += max_length;
// We hit a match, so add it to the output
int t = (max_distance - 1) & 0xFFF;
t |= (((max_length - 3) & 0xF) << 12);
tbuf.Add((byte)((t >> 8) & 0xFF));
tbuf.Add((byte)(t & 0xFF));
// Set the control bit
control |= (1 << (7 - i));
found = true;
}
}
if (!found)
{
// If we didn't find any strings, copy the byte to the output
tbuf.Add(data[address++]);
}
}
// Flush the temp buffer
obuf.Add((byte)(control & 0xFF));
obuf.AddRange(tbuf.ToArray());
}
return obuf.ToArray();
}
}
}

View File

@ -0,0 +1,175 @@
using System;
using System.Collections.Generic;
using System.IO;
using Newtonsoft.Json;
namespace RenderStaffCredits
{
class Program
{
static readonly ushort Palette = 0xF000;
static IDictionary<string, ushort> m12BigCharArrLookup;
static IDictionary<string, ushort> m12SmallCharArrLookup;
static ushort[] Arrangements;
static readonly ushort Empty = 0x9B;
static readonly ushort arrStart = 0x100;
static readonly byte defaultOffset = 0xD;
static readonly string defaultPlayerName = "MARIO";
static int player_Y_Pos = 0;
static void Main(string[] args)
{
//Load the stuff we'll use
string[] staff_text = File.ReadAllLines(args[0]);
string dataFolder = args[1] + Path.DirectorySeparatorChar;
m12BigCharArrLookup = JsonConvert.DeserializeObject<Dictionary<string, ushort>>(Asset.ReadAllText("m12-big-arr-lookup.json"));
m12SmallCharArrLookup = JsonConvert.DeserializeObject<Dictionary<string, ushort>>(Asset.ReadAllText("m12-small-arr-lookup.json"));
//Prepare the empty arrangements
Arrangements = createArrangements(getStaffTextLength(staff_text));
int pos = 0;
for (int i = 0; i < staff_text.Length; i++)
{
//Handle a single line and increment the current YPosition accordingly
pos += handleStr(staff_text[i], Arrangements, pos);
}
//Save the arrangements
File.WriteAllBytes(dataFolder + "m2-credits-arrangements_[c].bin", GBA.LZ77.Compress(convertUShortArrToByteArrLE(Arrangements)));
//Save some data that tells us where to put the player name at runtime
byte[] extra_things = new byte[4];
writeIntToByteArrLE(extra_things, player_Y_Pos, 0, 2);
writeIntToByteArrLE(extra_things, defaultPlayerName.Length, 2, 2);
File.WriteAllBytes(dataFolder + "m2-credits-extra-data.bin", extra_things);
//Save some data that tells us how many vertical tiles long the arrangement is
int arrSize = Arrangements.Length / 0x20;
byte[] size = new byte[4];
writeIntToByteArrLE(size, arrSize, 0, 4);
File.WriteAllBytes(dataFolder + "m2-credits-size.bin", size);
//Save some data that tells us where to end scrolling at runtime (in pixels)
int scrollSize = (arrSize + defaultOffset) * 8;
byte[] size_full = new byte[4];
writeIntToByteArrLE(size_full, scrollSize, 0, 4);
File.WriteAllBytes(dataFolder + "m2-credits-scroll-size.bin", size_full);
byte[] size_minus_one = new byte[4];
writeIntToByteArrLE(size_minus_one, scrollSize - 1, 0, 4);
File.WriteAllBytes(dataFolder + "m2-credits-scroll-size-limit.bin", size_minus_one);
}
static void writeIntToByteArrLE(byte[] arr, int value, int pos, int limiter)
{
for (int i = 0; i < limiter; i++)
arr[pos + i] = (byte)((value >> (8 * i)) & 0xFF);
}
static byte[] convertUShortArrToByteArrLE(ushort[] arr)
{
byte[] newArr = new byte[arr.Length * 2];
for (int i = 0; i < arr.Length; i++)
{
newArr[(i * 2)] = (byte)((arr[i]) & 0xFF);
newArr[(i * 2) + 1] = (byte)((arr[i] >> 8) & 0xFF);
}
return newArr;
}
static int handleStr(string str, ushort[] Arrangements, int YPosition)
{
if (str.StartsWith("# "))
{
handleSmallText(getStrContent(str), Arrangements, YPosition);
return 1;
}
if (str.StartsWith("- "))
{
handleBigText(getStrContent(str), Arrangements, YPosition);
return 2;
}
if (str.StartsWith("player_name"))
{
//Save data that tells us where to put the player_name at runtime
player_Y_Pos = YPosition;
handleBigText(defaultPlayerName, Arrangements, YPosition);
return 2;
}
if (str.StartsWith("> "))
return parseEmptyArrLine(getStrContent(str));
return 0;
}
static void handleBigText(string content, ushort[] Arrangements, int YPosition)
{
//The big text, normally, has a top tile and a bottom tile. The bottom tile is 0x20 tiles after the top tile
content = content.ToUpper();
int XPosition = getStrStartPos(content);
for (int i = 0; i < content.Length; i++)
{
string value = content[i].ToString();
if (m12BigCharArrLookup.ContainsKey(value))
{
Arrangements[(YPosition * 0x20) + XPosition + i] = (ushort)(Palette | (arrStart + m12BigCharArrLookup[value]));
Arrangements[(YPosition * 0x20) + XPosition + i + 0x20] = (ushort)(Palette | (arrStart + m12BigCharArrLookup[value] + 0x20));
}
}
}
static void handleSmallText(string content, ushort[] Arrangements, int YPosition)
{
content = content.ToUpper();
int XPosition = getStrStartPos(content);
for (int i = 0; i < content.Length; i++)
{
string value = content[i].ToString();
if (m12SmallCharArrLookup.ContainsKey(value))
Arrangements[(YPosition * 0x20) + XPosition + i] = (ushort)(Palette | (arrStart + m12SmallCharArrLookup[value]));
}
}
static int getStrStartPos(string str)
{
int len = getStrLen(str);
return 1 + ((0x1F - len) >> 1);
}
static int getStrLen(string str)
{
return str.Length;
}
static int getStaffTextLength(string[] staff_text)
{
int arrLen = 0;
for (int i = 0; i < staff_text.Length; i++)
{
string str = staff_text[i];
if (str.StartsWith("# "))
arrLen += 1;
else if (str.StartsWith("- ") || str.StartsWith("player_name"))
arrLen += 2;
else if (str.StartsWith("> "))
arrLen += parseEmptyArrLine(getStrContent(str));
}
return arrLen;
}
static string getStrContent(string str)
{
return str.Substring(2);
}
static int parseEmptyArrLine(string str)
{
return int.Parse(str);
}
static ushort[] createArrangements(int len)
{
ushort[] arrangements = new ushort[len * 0x20];
for (int i = 0; i < len; i++)
for (int j = 0; j < 0x20; j++)
arrangements[(i * 0x20) + j] = (ushort)(Palette | (Empty + arrStart));
return arrangements;
}
}
}

View File

@ -0,0 +1,9 @@
{
"profiles": {
"ScriptTool": {
"commandName": "Project",
"commandLineArgs": "-compile -main -misc \"..\\working\" \"..\\eb.smc\" \"..\\m12fresh.gba\"",
"workingDirectory": "C:\\Users\\jeffe\\M12\\bin\\ScriptTool"
}
}
}

View File

@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.1</TargetFramework>
<SuppressNETCoreSdkPreviewMessage>true</SuppressNETCoreSdkPreviewMessage>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="12.0.1" />
</ItemGroup>
<ItemGroup>
<None Update="m12-small-arr-lookup.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="m12-big-arr-lookup.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

View File

@ -0,0 +1,53 @@
{
" ": 74,
"!": 80,
"@": 81,
"\"": 91,
"'": 78,
",": 95,
"-": 79,
"_": 93,
".": 94,
"/": 77,
"0": 64,
"1": 65,
"2": 66,
"3": 67,
"4": 68,
"5": 69,
"6": 70,
"7": 71,
"8": 72,
"9": 73,
";": 84,
"?": 76,
"A": 0,
"B": 1,
"C": 2,
"D": 3,
"E": 4,
"F": 5,
"G": 6,
"H": 7,
"I": 8,
"J": 9,
"K": 10,
"L": 11,
"M": 12,
"N": 13,
"O": 14,
"P": 15,
"Q": 16,
"R": 17,
"S": 18,
"T": 19,
"U": 20,
"V": 21,
"W": 22,
"X": 23,
"Y": 24,
"Z": 25,
"[AC]": 88,
"~": 87,
"[AF]": 89
}

View File

@ -0,0 +1,30 @@
{
"A": 128,
"B": 129,
"C": 130,
"D": 131,
"E": 132,
"F": 133,
"G": 134,
"H": 135,
"I": 136,
"J": 137,
"K": 138,
"L": 139,
"M": 140,
"N": 141,
"O": 142,
"P": 143,
"Q": 144,
"R": 145,
"S": 146,
"T": 147,
"U": 148,
"V": 149,
"W": 150,
"X": 151,
"Y": 152,
"Z": 153,
".": 154,
" ": 155
}

295
working/staff_text.md Normal file
View File

@ -0,0 +1,295 @@
- STAFF
- _____
> 6
# PRODUCED AND DIRECTED BY
- SHIGESATO ITOI
> 2
# WRITTEN BY
- SHIGESATO ITOI
> 2
# MUSIC BY
- KEIICHI SUZUKI
- HIROKAZU TANAKA
> 6
# GAME DESIGNER
- AKIHIKO MIURA
> 2
# ART DIRECTOR
- KOUICHI OOYAMA
> 2
# SOUND DIRECTOR
- HIROKAZU TANAKA
> 2
# PROGRAM DIRECTOR
- SATORU IWATA
> 2
# U.S. CONVERSION DIRECTOR
- KOUJI MALTA
> 2
# TRANSLATION DIRECTORS
- MARCUS LINDBLOM
- MASAYUKI MIURA
> 6
# PROGRAMMERS
- SATORU IWATA
- SATOSHI MITSUHARA
- YOSHIMI TAKAHASHI
- SEIJI OTOGURO
- BOU NAKAJIMA
- SEIKA ABE
- YOSHIKI SUZUKI
- JUN YAMAZAKI
- TOSHIYUKI UENO
- TAKASHI SASAKI
- TAKASHI SAKUMA
- KAZUO SATO
- KOUJI MALTA
> 2
# SOUND PROGRAMMER
- HIROKAZU TANAKA
> 2
# SYSTEM ENGINEERING
- MAKOTO KANAI
- TAKASHI SAKUMA
> 6
# GRAPHICS ARTISTS
- KOUICHI OOYAMA
- HIROYUKI SAKIYAMA
- KOUICHI YAMADA
- MAKIO KATAOKA
- TETSUYA NOTOYA
- YASUNORI YANAGISAWA
> 2
# SPECIAL EFFECTS ARTIST
- TSUNEKAZ ISHIHARA
> 2
# FONT DESIGNERS
- AKIHIKO MIURA
- KOUICHI OOYAMA
> 2
# CONCEPT OF SATURN FONT BY
- SHIGESATO ITOI
> 6
# GRAPHICS DATA MANAGER
- YASUNORI YANAGISAWA
> 2
# MESSAGE DATA MANAGERS
- MASAYUKI MIURA
- HITOSHI MATSUI
> 6
# ADDITIONAL MUSIC COMPOSED BY
- HIROSHI KANAZU
- TOSHIYUKI UENO
> 2
# SOUND STAFF
- TOSHIYUKI UENO
- KOZUE ISHIKAWA
> 6
# ASSISTANT PROGRAMMERS
- IKUHO HAGIYA
- KATSUYOSHI IRIE
> 2
# ASSISTANT GAME DESIGNERS
- KOUICHI OOYAMA
- TAKEHIKO MASUDA
- CHIAKI YOSHIZAWA
- MASAYUKI MIURA
- AKIHITO TODA
- HITOSHI MATSUI
- KATSUYOSHI IRIE
> 2
# ASSISTANT SCENARIO WRITERS
- MASAYUKI MIURA
- AKIHITO TODA
- HIROYUKI JINNAI
> 2
# ENGLISH TEXT WRITERS
- MARCUS LINDBLOM
- DAN OWSEN
> 2
# TRANSLATORS
- KEIKO TAMURA
- YUKA NAKATA
> 6
# CHIEF DEBUGGERS
- TAKUMI AKABANE
- HITOSHI MATSUI
> 6
# NOA DEBUG COORDINATORS
- SEAN O'CONNOR
- MICHAEL KELBAUGH
> 6
# APE DEBUGGING TEAM
- TAKAHIRO NAGAI
- JUNICHI AKAMA
- AKIRA MATSUMOTO
> 6
# NINTENDO DEBUG COORDINATORS
- TOHRU HASHIMOTO
- TATSUYA HISHIDA
> 2
# NINTENDO DEBUGGING TEAM
- KAZUHIDE OHGOE
- ATSUSHI MIYAKE
- HAJIME NAKAMURA
- MAO HAMAMOTO
- MAYUMI TADA
- MAKOTO KEDOUIN
- KIMIKO TSUCHIDA
- SAYAKA KOMURA
> 2
# MAP CHECKERS
- MAPPER KOJIMA
- KATSUYOSHI IRIE
> 6
# SAMPLING VOICES
- SHIGESATO OK? ITOI
- YUKARI WHISTLE SAITO
- HIROKAZU BELCH KOYANO
- ETSUKO VENUS KAWANO
- TAKASHI MU WATANABE
> 2
# OPENING ROCK GUITAR
- M.D.SEEGER
> 6
# HARDWARE SUPPORT
- HIRONOBU KAKUI
- SHIGEKI YAMASHIRO
> 6
# PRODUCTION MANAGERS
- TAKASHI KAWAGUCHI
- KEIZO KATO
- IKUO HYAKUTA
- YUKARI SAITO
> 2
# COORDINATORS
- TAKASHI WATANABE
- RYO KAGAWA
- HIROYUKI JINNAI
- ETSUKO KAWANO
- KAZUYUKI YAMAMOTO
- ATSUKO KAWAHARA
> 6
# SOUND PRODUCERS
- AKIO OHMORI
- RITSUO KAMIMURA
> 6
# SPECIAL THANKS TO
- KENJI ANDO
- MIYUKI KURE
- TAKAYUKI ONODERA
- FUKASHI OMORITA
- YUKIO TAKAHASHI
- KAZUHIKO AMEMIYA
- YASUHIRO KUMAGAI
- YOSHIO HONGO
- NAOKO KANAZAWA
- BENIMARU ITOH
- MOTOHIRO ISHII
- HIROKAZU KOYANO
- MASAO TOTTORI
- NOBUYASU MAKINO
- HIROMI TAMAGAWA
- NANCY YOSHITAKE
> 2
- AND MANY OTHERS
> 6
# THE PRODUCERS WISH TO THANK
- ISHEL ARTVISION
- MOONRIDERS OFFICE
- SEDIC INC.
- SETA CO.,LTD.
- LUCKY NICE
- OKUBO DESIGN STUDIO
- ON ASSOCIATES
- SAI KOUBOU
- ASHURA OFFICE
- SHOGAKUKAN
- SHINCHO SHA
> 6
# CHIEF COORDINATOR
- MARCUS LINDBLOM
> 6
# LINE PRODUCER
- TSUNEKAZ ISHIHARA
> 6
# COPRODUCER
- SATORU IWATA
> 6
# NOA PRODUCER
- MIKE FUKUDA
> 6
# SUPERVISOR
- SHIGERU MIYAMOTO
> 6
# EXECUTIVE PRODUCERS
- HIROSHI YAMAUCHI
- MINORU ARAKAWA
> 6
# PRESENTED BY
- NINTENDO
> 2
# IN ASSOCIATION WITH
- APE INC.
> 2
# AND
- HAL LABORATORY,INC.
> 12
# AND...
#
> 14
# PLAYER
player_name
> 1