Register - Login
Views: 99823695
Main - Memberlist - Active users - Calendar - Wiki - IRC Chat - Online users
Ranks - Rules/FAQ - Stats - Latest Posts - Color Chart - Smilies
05-03-22 07:35:23 PM
Jul - NO! GO TO STAR! - Identifying the ROM Data New poll - New thread - Thread closed
Next newer thread | Next older thread
GuyPerfect
Catgirl
Level: 68


Posts: 86/1096
EXP: 2665801
For next: 62999

Since: 07-23-07


Since last post: 1.7 years
Last activity: 220 days

Posted on 10-07-07 11:13:23 PM; last edit by GuyPerfect on 10-18-07 03:11 AM Link
DISCLAIMER:
Do not, in any way, attempt to distribute ROMs or offer to give ROMs to other people. This board does not endorse illegal activity, and it is expressly forbidden to you to partake in it. Make your own backup ROMs using your own, legaly purchased game cartridge. Don't steal from someone else.

Disclaimer out of the way, there's a number of different F-Zero X ROMs out there, many of them hacked versions like most N64 games. You'll often find one ending with a [!] which is a de facto symbol representing "good dump." This level editor will support [!] ROMs and nothing else, so make sure you have an appropriate version.

There are currently seven ROMs for F-Zero X defined by the emulation community. They are as follows:

* F-Zero_X_(J)_[!].z64 - The original Japanese version
* F-Zero_X_(U)_[!].z64 - The North American version
* F-Zero_X_(E)_[!].z64 - The PAL edition; given E for "European"
* F-Zero_X_(J)_[!].v64, F-Zero_X_(U)_[!].v64, F-Zero_X_(E)_[!].v64 - As above, but in V64 format.
* F-Zero_X_Expansion_Kit_(J)_[!].ndd - The Expansoin Kit disk. We'll be using this, but not right now.

The Dr. V64 backup devices for Nintendo 64 have a known quirk in that every pair of bytes is reversed in ROM files made with that technology. That is to say, if normal byte ordering is ABCD, then a V64 ROM would show BADC. This is known as "byte swapping," and is important to take into account when working with N64 games.
__________

If you were to write a program to determine which version of the ROM is being loaded, this is what you'd do:

* F-Zero X is a 16MB ROM. That means that any valid F-Zero X ROM file must be exactly 16,777,216 bytes in length. If it is not, well... it's either not F-Zero X or some modified variant of the ROM that you shouldn't be expected to support.
* Every N64 ROM has an internal name set by, I believe, Nintendo themselves when a game was published. The name is located at byte 0x20 in the ROM and is 20 bytes long. Now, that first one's a hexadecimal 20 and the second one's a decimal 20. Don't get them confused. F-Zero X's internal name is "F-ZERO X" padded with 12 spaces at the end. If that's the string you found in the ROM, it's Z64 format. If it shows up as "-FEZORX ", then it's V64 format and every two bytes swap position with each other.
* At offset 0x3E in the ROM for Z64, and 0x3F for V64, is a single byte specifying which region the game was targeted to. It's a single, ASCII character to make life easier. Technically, it's part of the game's identifier code, but that one character is all we need to determine version. "J" is Japan, "E" is North America (English), and "P" is PAL. This may seem like a trivial piece of information, but each version of the ROM has data in different spots, so it's a crucial bit of knowledge if you want to get anywhere.
__________

What we'll need to do first thing before we can do anything else is to have a function that can load a ROM file into memory. If anyone has experience in programming, this is what I want, programmed in C. Keep in mind that it must be your own work and will be licensed under the project's license.




Since I botched up the code request by not thinking it through well enough, I'll give you guys a freebie and post a definition of that function right now:

--Code moved to later post--




So then... Next, we'll need a function that can identify the ROM version. It should check file size, ROM name string, ID region character and byte ordering (V64 or otherwise) as described earlier in this post.

Function prototype:
char fzxROMVersion(unsigned char *ROMData, unsigned long ROMSize)
ROMData - Pointer to the ROM data in memory.
ROMSize - Size of the ROM file in bytes.

Description:
Checks ROM data to determine version and format of F-Zero X, if any.

Return value:
Returns a pre-defined code designating the detected version of F-Zero X. J, U and P specify Japanese, North American and PAL versions, respectively. Z and V specify normal byte ordering (Z64) and byte-swapped (V64) respectively.

SymbolValue
FZX_VER_NotFZX0
FZX_VER_JZ1
FZX_VER_UZ2
FZX_VER_PZ3
FZX_VER_JV4
FZX_VER_UV5
FZX_VER_PV6
Rena
I had one (1) message in Discord deleted and proceeded to make a huge, huge mess about how it was a violation of free speech and how moderators are supposed to be spam janitors and nobody should have the right to tell me not to talk about school shootings
Level: 135


Posts: 1263/5390
EXP: 29076961
For next: 258044

Since: 07-22-07

Pronouns: he/him/whatever
From: RSP Segment 6

Since last post: 342 days
Last activity: 342 days

Posted on 10-08-07 07:59:52 AM Link
JL2 - Post #1263 - 10-08-07 02:59:52 AM
Day 77, rank 7; Level 39 (73.8%)
25323/34324 (395770/404771)
GPP: 470; GT: 33.945
Originally posted by GuyPerfect
FZX_VER_NotFZX
Might I suggest FZX_VER_INVALID? It seems more logical and you keep the uppercase that way.

____________________
GuyPerfect
Catgirl
Level: 68


Posts: 89/1096
EXP: 2665801
For next: 62999

Since: 07-23-07


Since last post: 1.7 years
Last activity: 220 days

Posted on 10-08-07 11:21:15 PM; last edit by GuyPerfect on 10-18-07 03:11 AM Link
I was going for concision. I settled on FZX_VER_BAD since then.

But why would someone like you comment on the name of a constant instead of provide a definition for the function I described?

Anyways, I did. It's below. For anyone reading this, if you have programming experience or know anyone who does, it'll be great to get some more people coding in this project. We don't need a whole convention center's worth, but I don't want to end up being the only one.



The promised definition:

--Code moved to later post--



The next one should be fairly easy. If a ROM is in V64 byte ordering, we'll need to clear it up to make it good data. So we'll need a function to do that, as follows:

Function prototype:
void fzxByteSwap(unsigned char *ROMData, unsigned long ROMSize);
ROMData - Pointer to the location in memory where the ROM data was loaded
ROMSize - Size of the ROM specified by ROMData

Description:
Byte-swaps a ROM loaded in memory. Data is swapped in 16-bit units, where the two bytes of each unit are replaced with the other. For example, "ABCD" will become "BADC".
smkdan
User
Level: 12


Posts: 4/21
EXP: 7067
For next: 854

Since: 07-28-07


Since last post: 13.0 years
Last activity: 13.2 years

Posted on 10-13-07 01:06:29 PM Link
I'll bite.

void fzxByteSwap(unsigned char *ROMData, unsigned long ROMSize)
{
int index = 0;
unsigned char temp1, temp2;

while(index < ROMSize)
{
temp1 = ROMData[index];
temp2 = ROMData[index +1];

ROMData[index] = temp2;
ROMData[index +1] = temp1;

index +=2;
}
}


The tabs don't show but it's so small you don't really need them.
Rena
I had one (1) message in Discord deleted and proceeded to make a huge, huge mess about how it was a violation of free speech and how moderators are supposed to be spam janitors and nobody should have the right to tell me not to talk about school shootings
Level: 135


Posts: 1306/5390
EXP: 29076961
For next: 258044

Since: 07-22-07

Pronouns: he/him/whatever
From: RSP Segment 6

Since last post: 342 days
Last activity: 342 days

Posted on 10-13-07 10:29:56 PM; last edit by HyperHacker on 10-13-07 10:31 PM Link
JL2 - Post #1306 - 10-13-07 05:29:56 PM
Day 83, rank 7; Level 40 (71.5%)
26120/36538 (430891/441309)
GPP: 494; GT: 33.426
Optimization go!
void fzxByteSwap(unsigned char *ROMData, unsigned long ROMSize)
{
unsigned int index = 0;
unsigned char temp;

while(index < ROMSize)
{
temp = ROMData[index];
ROMData[index] = ROMData[index + 1];
ROMData[index + 1] = temp;
index += 2;
}
}


____________________
GuyPerfect
Catgirl
Level: 68


Posts: 106/1096
EXP: 2665801
For next: 62999

Since: 07-23-07


Since last post: 1.7 years
Last activity: 220 days

Posted on 10-13-07 11:50:28 PM; last edit by GuyPerfect on 10-18-07 03:12 AM Link
Excellent. Good to see some other people stepped up to the plate. What we have here is exactly what I wanted to have happen all along. smkdan, you posted a function that you created, which shows that you have an understanding of what needs to be done. HyperHacker, you shared an insight about the code so that smkdan and others can see how they may improve. That's the kind of thing I want to see in this project.

My personal version of this function used a For loop, but at the end of the day, it's the same net effect. Note: I've changed the spacing and positioning of the { braces to conform to the style I used in earlier snippits. If this becomes an issue, we may discuss it. But for the sake of consistency, I've put the {'s at the ends of the previous lines.

If anyone's curious how I post these code blocks, I use some custom HTML formatting inside
 tags.


--Code moved to later post--



Okay then. With the ability to load a ROM file from hard disk into memory, check the loaded data to see if it can be identified as F-Zero X, and the ability to byte-swap the data if it's detected as being in V64 format, we're just about done.

There are various hacked versions of the game out there, and anyone doing his own hacking can easily created a hacked version himself. Since this level editor will be built specifically to work with the original ROM data as it came from Nintendo, we'll need to run one last check to make sure we aren't using modified ROMs. This check will be a simple pass of the CRC-32 algorithm.

I won't ask you guys to implement the CRC check, as code for the algorithm tends to be complicated and uses a pre-defined list of lookup values that take up unnecessary source file space. I have code that can generate the tables as well as calculate the checksum all in just a few lines, so I'll be implementing this myself.

If anyone has some super-awesome idea for this, however, go ahead and say so.




EDIT:
I've got the CRC code ready. Call fzxCRC() with the pointer to the ROM data and it will calculate its CRC. ROM data must be normal byte ordering, so byte-swap if it's V64. fzxCRC() will call crcGenTable() as well as crcCalculate(). The CRC code I provided here can easily be modified to give header checksum calculators in Nintendo DS ROMs, so it's an added bonus.

--Code moved to later post



Okay, then. All we need now is one more function and we'll be done with identification.

Functon prototype:
char fzxCheckROM(unsigned char *ROMData, char Version);
ROMData - Pointer in memory to the ROM data. Is assumed to be 0x1000000 bytes.
Version - Value returned by fzxROMVersion()

Description:
Detects if an F-Zero X ROM is an original dump or a hack, and returns the version of the data. The following CRCs will be used to identify ROMs:
FZX_CRC_J 0x6B1CEF83
FZX_CRC_U 0x0B561FBA
FZX_CRC_P 0x2D6F7E8B


Return Value:
Function returns a predefined value that identifies the data in the ROM. Possible return values are the following:
FZX_ROM_BAD 0
FZX_ROM_J 1
FZX_ROM_U 2
FZX_ROM_P 3
FZX_ROM_HACK 4


Remarks:
If the ROM data is detected as being in V64 format, it must be byte-swapped before this function is called.
paulguy

Green Birdo
Level: 93


Posts: 7/2294
EXP: 8032759
For next: 20051

Since: 09-14-07

From: Buffalo, NY

Since last post: 9.7 years
Last activity: 9.7 years

Posted on 10-18-07 12:32:30 AM; last edit by paulguy on 10-18-07 12:32 AM Link
I'm not so good at coding and this may contain typos but here's my attempt:



char fzxCheckROM(unsigned char *ROMData, char Version) {
switch(Version) {
case FZX_VER_BAD:
return FZX_ROM_BAD;
case FZX_VER_JZ:
case FZX_VER_JV:
if(fzxCRC(ROMData) == 0x6B1CEF83) {
return FZX_ROM_J;
} else {
return FZX_ROM_HACK;
}
case FZX_VER_UZ:
case FZX_VER_UV:
if(fzxCRC(ROMData) == 0x0B561FBA) {
return FZX_ROM_U;
} else {
return FZX_ROM_HACK;
}
case FZX_VER_PZ:
case FZX_VER_PV:
if(fzxCRC(ROMData) == 0x2D6F7E8B) {
return FZX_ROM_P;
} else {
return FZX_ROM_HACK;
}
}
}
}

GuyPerfect
Catgirl
Level: 68


Posts: 117/1096
EXP: 2665801
For next: 62999

Since: 07-23-07


Since last post: 1.7 years
Last activity: 220 days

Posted on 10-18-07 03:12:27 AM Link
Excellent. By using a switch statement, you were able to cut out most of the overhead that wasn't required for any given version of the ROM. The only thing I changed here, other than some spacing considerations, was that I removed the check for FZX_VER_BAD and simply stuck a "return FZX_VER_BAD" at the end of the function. Always be sure to have a function returning a value.



I've removed all of my other code postings, and have merged them all into this post. What I have here is something that you can use to compile your own F-Zero X ROM identifier! I've structured it so that it can be used as a sort of API for the project.

I've got three files here.

fzxROM.h
// Data ID constants

#define FZX_ROM_BAD 0
#define FZX_ROM_J 1
#define FZX_ROM_U 2
#define FZX_ROM_P 3
#define FZX_ROM_HACK 4

// ROM version/format constants
#define FZX_VER_BAD 0
#define FZX_VER_JZ 1
#define FZX_VER_UZ 2
#define FZX_VER_PZ 3
#define FZX_VER_JV 4
#define FZX_VER_UV 5
#define FZX_VER_PV 6

// Function prototypes
void fzxByteSwap(unsigned char *, unsigned long);
char fzxCheckROM(unsigned char *, char);
unsigned char *fzxLoadROM(char *, unsigned long *);
char fzxROMVersion(unsigned char *, unsigned long);


fzxROM.c
// Includes

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "fzxROM.h"

// CRCs
#define FZX_CRC_J 0x6B1CEF83
#define FZX_CRC_U 0x0B561FBA
#define FZX_CRC_P 0x2D6F7E8B

// Generates a CRC 8-bit lookup table
void crcGenTable(unsigned int *CRCTable, unsigned int Poly) {
unsigned int A, B, Reg;

// Cycle through indexes
for (A = 0; A < 256; A++) {
Reg = A;

// Cycle through bits
for (B = 0; B < 8; B++) {
if (Reg & 1) // Shift and XOR with mask
Reg = (Reg >> 1) ^ Poly;
else // Just shift right
Reg >>= 1;
}

// Set value in table
CRCTable[A] = Reg;
}

return;
}

// Calculate the CRC of a range of bytes
unsigned int crcCalculate(unsigned char *Buff, unsigned long Leng,
unsigned int *Lookup) {
unsigned int CRC = 0xFFFFFFFF; // 0xFFFF for NDS CRC-16
unsigned long I;

// Process bytes of input data
for (I = 0; I < Leng; I++)
// HighBytes XOR Lookup[LowByte XOR Input]
CRC = (CRC >> 8) ^ Lookup[(CRC & 0xFF) ^ Buff[I]];

return CRC ^ 0xFFFFFFFF; // Omit the XOr operation for NDS CRC-16.
}

// Calculates the CRC of an F-Zero X ROM
unsigned int fzxCRC(unsigned char *ROMData) {
unsigned int Lookup[256];

// Generate the lookup table.
// This should ideally be done once and with a global array.

crcGenTable(Lookup, 0xEDB88320); // 0x04C11DB7 backwards in binary
//CRCGenTable(Lookup, 0xA001); // 0x8005 for CRC-16

// Caluclate the CRC

return crcCalculate(ROMData, 0x1000000, Lookup);
}

// Byte-swaps a ROM
void fzxByteSwap(unsigned char *ROMData, unsigned long ROMSize) {
unsigned long index = 0;
unsigned char temp;

// Swap bytes in pairs
while (index < ROMSize) {
temp = ROMData[index];
ROMData[index] = ROMData[index + 1];
ROMData[index + 1] = temp;
index += 2;
}

return;
}

// Checks a ROM for hackage
char fzxCheckROM(unsigned char *ROMData, char Version) {
switch (Version) {

// Check for Japanese version
case FZX_VER_JZ: case FZX_VER_JV:
if (fzxCRC(ROMData) == FZX_CRC_J)
return FZX_ROM_J;
else
return FZX_ROM_HACK;

// Check for North American version
case FZX_VER_UZ: case FZX_VER_UV:
if (fzxCRC(ROMData) == FZX_CRC_U)
return FZX_ROM_U;
else
return FZX_ROM_HACK;

// Check for PAL version
case FZX_VER_PZ: case FZX_VER_PV:
if (fzxCRC(ROMData) == FZX_CRC_P)
return FZX_ROM_P;
else
return FZX_ROM_HACK;
}

// Everything else
return FZX_ROM_BAD;
}

// Reads the contents of a file into memory
unsigned char *fzxLoadROM(char *Filename, unsigned long *FileSize) {
FILE *FilePtr = NULL;
unsigned char *Buffer;

// Open the file
FilePtr = fopen(Filename, "rb");
if (FilePtr == NULL)
return NULL;

// Seek to the end of the file
if (fseek(FilePtr, 0, SEEK_END))
{ fclose(FilePtr); return NULL; }

// Get the file size
*FileSize = ftell(FilePtr);
if (!*FileSize) // Bad file size
{ fclose(FilePtr); return NULL; }

// Seek back to the beginning of the file
if (fseek(FilePtr, 0, SEEK_SET))
{ fclose(FilePtr); return NULL; }

// Allocate the memory
Buffer = malloc(*FileSize);
if (Buffer == NULL)
{ fclose(FilePtr); return NULL; }

// Read the data from the file into the memory
if (!fread(Buffer, 1, *FileSize, FilePtr))
{ free(Buffer); fclose(FilePtr); return NULL; }

// Close the file and exit
fclose(FilePtr);
return Buffer;
}

// Detects the version and format of an F-Zero X ROM
char fzxROMVersion(unsigned char *ROMData, unsigned long ROMSize) {
char NameString[21] = "";
char Temp = 0, V = 0;

// Check ROM size
if (ROMSize != 0x1000000)
return FZX_VER_BAD;

// Check ROM name string
strncpy(NameString, &(ROMData[0x20]), 20);
if (!strcmp(NameString, "F-ZERO X ")) Temp = 4; // Z64
if (!strcmp(NameString, "-FEZORX ")) Temp = 3; // V64
if (!Temp) // Name string invalid
return FZX_VER_BAD;

// Get region ID
V = Temp & 3;
if (!V) Temp = ROMData[0x3E]; // Z64
else Temp = ROMData[0x3F]; // V64

// Check region ID
switch (Temp) {
case 'J': return FZX_VER_JZ + V;
case 'E': return FZX_VER_UZ + V;
case 'P': return FZX_VER_PZ + V;
}

// Bad region ID if we get here
return FZX_VER_BAD;
}


Driver.c
#include <stdio.h>

#include <stdlib.h>
#include "fzxROM.h"

int main(int argc, char **argv) {
unsigned char *ROM = NULL;
unsigned long ROMSize;
char Version;

// Display usage if incorrect format
if (argc != 2) {
printf("Usage: fzxROM <filename>\n");
return 0;
}

// Load the ROM
ROM = fzxLoadROM(argv[1], &ROMSize);
if (ROM == NULL) {
printf("Error loading file.\n");
return 1;
}

// Like a loading screen, but not quite
printf("ROM Version: ...\rROM Version: ");

// Get ROM version and byte-swap data if need be
Version = fzxROMVersion(ROM, ROMSize);
if (Version > FZX_VER_PZ)
fzxByteSwap(ROM, ROMSize);
Version = fzxCheckROM(ROM, Version);

// Deallocate the memory
free(ROM);

// Output ROM version
switch (Version) {
case FZX_ROM_BAD: printf("(Not F-Zero X)\n"); break;
case FZX_ROM_J: printf("Japanese\n"); break;
case FZX_ROM_U: printf("North American\n"); break;
case FZX_ROM_P: printf("PAL\n"); break;
case FZX_ROM_HACK: printf("(Hacked ROM)\n"); break;
}

return 0;
}
Next newer thread | Next older thread
Jul - NO! GO TO STAR! - Identifying the ROM Data New poll - New thread - Thread closed


Rusted Logic

Acmlmboard - commit 47be4dc [2021-08-23]
©2000-2022 Acmlm, Xkeeper, Kaito Sinclaire, et al.

30 database queries, 3 query cache hits.
Query execution time: 0.081309 seconds
Script execution time: 0.038478 seconds
Total render time: 0.119787 seconds