StarTrader wrote:
Duhhhh……OK, I give up!
Where is it??!!
Huh? It’s right there under the Downloads section…2nd item under the Freelancer Critical Downloads section as of the posting of this post.
– The Haen.
StarTrader wrote:
Duhhhh……OK, I give up!
Where is it??!!
Huh? It’s right there under the Downloads section…2nd item under the Freelancer Critical Downloads section as of the posting of this post.
– The Haen.
The upload section seems to be working again. I’ve submitted the new version that should appear soon pending admin approval.
No big changes in the new version: I fixed a minor bug that caused output redirected to a text file to look odd, and I also expanded the help section to be a bit more…helpful.
The updated source code is included with the file, and I’ll also be updating the source code located on page 1 of this thread very shortly.
– The Haen.
Uploads seem to be working again, so I’ve uploaded the file onto the site. It should be there soon pending admin approval.
– The Haen.
FriendlyFire wrote:
A suggestion, perhaps, Haen: if you have a help command, maybe default to its output if not arguments are given? Or at least, put a “type –help for more information” in the error message.It’s something simple, but it can help a lot with usability
Actually, the tool already does that: if it detects that the wrong number of arguments were supplied, it prints the error message and then runs the usage function. The usage function, in turn, prints out the proper syntax for the tool.
Having said that, I can see perhaps the usage function may be better in conveying its message. Since I’ll be re-uploading FLFacHash to fix the output formatting issues, I’ll go ahead and write up a better help message. Thanks for the suggestion.
On a related note: any timetable on when the ability to upload will be fixed?
Cheers,
– The Haen.
Dragnite_MU wrote:
It’s a command-line program! Try running it through the command host (cmd)
tried that and it tells me
Error, wrong number of arguments
whatever that means
That means you need to give FLFacHash a faction ID to process. This error message appears if you entered the FLFacHash in the command prompt by itself without supplying any IDs with which it can work.
For example, if you want find out the hash code for the Kusari State Police, then you’d enter the following in the command line:
C:\>FLFacHash ku_p_grp
This should return the hashcode of 552.
– The Haen.
M0tah wrote:
For not being a coder that’s excellent code; great work and thanks for sharing!One suggestion for future posts: enclosing the code in```
tags preserves the formatting of the text so indentation isn’t lost.Oops…can’t believe I forgot about the formatting. This has now been fixed. Thanks, M0tah.
– The Haen.
For the curious, here’s the source code, written in (hopefully) ANSI C. I probably could have coded several things more efficiently, but then again, I’m no coder. Feel free to modify as you see fit.
/*
Name: FLHash, version 2.00
Author: Haenlomal
Date: November 2009
Purpose: Returns hashcodes for various Freelancer objects
All credit goes to Sherlog for discovering the initial Freelancer
hash algorithm as it was implemented in the original FLHash.exe.
This is free software. Permission to copy, store and use granted as long
as this copyright note remains intact.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#define _VERSION_STAMP 2.00.00
// The hash table used for generic entities and locations is a modified
// CRC-16-IBM Lookup Table using the reversed little-endian polynomial
// of 0xA001, extended logically from its original 16-bit specification
// to a 32-bit implementation.
#define CRC16IBM_POLYNOMIAL 0xA001
// The hash table used for factions is the standard CRC-16-CCITT Lookup Table
// using the standard big-endian polynomial of 0x1021.
#define CRC16CCITT_POLYNOMIAL 0x1021
// Constants for both LUTs
#define NUM_BITS 8
#define HASH_TABLE_SIZE (1 << NUM_BITS)
// Error code defines
#define SUCCESSFUL_RUN 0
#define WRONG_NUMBER_ARGUMENT 1
#define INVALID_ARGUMENT 2
#define MALLOC_FAILED 3
#define UNKNOWN_ERROR 4
// Global LUT declarations
unsigned short crc_16_ccitt_table[HASH_TABLE_SIZE];
unsigned long modded_crc_16_ibm_table[HASH_TABLE_SIZE];
void usage( const char* prog, int i )
{
FILE* s;
s = i ? stdout : stderr;
fprintf( s, "Usage: %s [-e | -f | -h | -l] [nicknames] ...\r\n", prog );
fprintf( s, "\r\n" );
fprintf( s, "%s will print the hash for the supplied nickname(s) in unsigned, signed\r\n", prog );
fprintf( s, "and hex format (comma-delimited list), hashed according to the given flag(s).\r\n" );
fprintf( s, "Flags and nicknames are not case sensitive, and specifying two hash flags in\r\n" );
fprintf( s, "a row will result in the first flag being silently ignored.\r\n" );
fprintf( s, "\r\n" );
fprintf( s, "Flags:\r\n" );
fprintf( s, "\r\n" );
fprintf( s, "-e\tIndicates that all nicknames following the flag are normal Freelancer\r\n" );
fprintf( s, "\tobjects, unless overridden by another flag further down the argument\r\n" );
fprintf( s, "\tchain. %s will hash the nicknames using the default algorithm. If\r\n", prog );
fprintf( s, "\tno flags are specified, this is the default mode.\r\n" );
fprintf( s, "-f\tIndicates that all nicknames following the flag are Freelancer\r\n" );
fprintf( s, "\tfaction IDs, unless overridden by another flag further down the\r\n" );
fprintf( s, "\targument chain. %s will hash the nicknames using the Faction ID\r\n", prog );
fprintf( s, "\thash algorithm.\r\n" );
fprintf( s, "-h\tBrings up usage instructions and exits %s.\r\n", prog );
fprintf( s, "-l\tIndicates that all nicknames following the flag are Freelancer base\r\n" );
fprintf( s, "\tlocations, unless overridden by another flag further down the argument\r\n" );
fprintf( s, "\tchain. The nicknames MUST be in the following format:\r\n" );
fprintf( s, "\t\"<nickname_of_base>,<nickname_of_room>\", or %s will return an\r\n", prog );
fprintf( s, "\terror. %s will hash the nicknames using the Location ID hash\r\n", prog );
fprintf( s, "\talgorithm.\r\n" );
fprintf( s, "\r\n" );
fprintf( s, "Examples:\r\n" );
fprintf( s, "\r\n" );
fprintf( s, "\"%s -e Li01 Li02\"\r\n", prog );
fprintf( s, "\tThis would return the hashcodes for \"Li01\" and \"Li02\", using the\r\n" );
fprintf( s, "\tdefault hash algorithm.\r\n" );
fprintf( s, "\r\n" );
fprintf( s, "\"%s Li01 Li02\"\r\n", prog );
fprintf( s, "\tThis would return the same result as in the first example, since the\r\n" );
fprintf( s, "\t-e flag is optional.\r\n" );
fprintf( s, "\r\n" );
fprintf( s, "\"%s -f li_p_grp gd_bh_grp\"\r\n", prog );
fprintf( s, "\tThis would return the hascodes for \"li_p_grp\" and \"gd_bh_grp\", using\r\n" );
fprintf( s, "\tthe Faction ID hash algorithm.\r\n" );
fprintf( s, "\r\n" );
fprintf( s, "\"%s -l Li01_01_Base,Cityscape Li03_02_Base,Deck\"\r\n", prog );
fprintf( s, "\tThis would return the hashcodes for the \"Cityscape\" room in base\r\n" );
fprintf( s, "\t\"Li01_01_Base\" and the \"Deck\" room in base \"Li03_02_Base\", using the\r\n" );
fprintf( s, "\tLocation ID hash algorithm.\r\n" );
fprintf( s, "\r\n" );
fprintf( s, "\"%s Li01 Li02 -f li_p_grp gd_bh_grp -l Li01_01_Base,Cityscape -e Ku01\"\r\n", prog );
fprintf( s, "\tThis would return the default hashcodes for \"Li01\", \"Li02\", and \"Ku01\",\r\n" );
fprintf( s, "\treturn the Faction ID hashcodes for \"li_p_grp\" and \"gd_bh_grp\", and\r\n" );
fprintf( s, "\tfinally the Location hashcode for the \"Cityscape\" room in base\r\n" );
fprintf( s, "\t\"Li01_01_Base\". Note how each option specified changed how the\r\n" );
fprintf( s, "\tnicknames following them would be interpreted.\r\n" );
}
void Initialize_CRC_16_CCITT_Table()
{
int i, j;
unsigned short x;
for ( i = 0 ; i < HASH_TABLE_SIZE ; i++ )
{
x = i << ( 16 - NUM_BITS );
for ( j = 0; j < NUM_BITS ; j++)
{
x = ( x & 0x8000 ) ? ( x << 1 ) ^ CRC16CCITT_POLYNOMIAL : ( x << 1 );
}
crc_16_ccitt_table[i] = x;
}
}
void Initialize_Modded_CRC_16_IBM_Table()
{
int i, j;
unsigned long x;
for ( i = 0; i < HASH_TABLE_SIZE ; i++ )
{
x = i;
for (j = 0;j < 16 - NUM_BITS ; j++ )
{
x = ((x & 1) == 1) ? (x >> 1) ^ (CRC16IBM_POLYNOMIAL << 14) : x >> 1;
}
modded_crc_16_ibm_table[i] = x;
}
}
char* HexToString( unsigned long l )
{
unsigned long i, j, k;
char* p;
char* q;
// Determine length of hex string.
// Since input is unsigned long,
// the max length of the hex
// string is 8\. So force a stop
// if we reach 8.
i = 0;
j = 1;
while ( ( l > j ) && ( i < 8 ) )
{
i += 1;
j *= 16;
}
// Malloc length + 1\. Freeing memory is
// entirely the responsibility of the
// caller!
p = malloc( i + 1 );
if ( p == NULL )
{
fprintf( stderr, "Error: malloc() failed.\r\n" );
exit( MALLOC_FAILED );
}
q = p;
for ( k = 1 ; k <= i ; k++ )
{
j = ( l >> ( ( i - k ) * 4 ) ) & 0xF;
j = ( j < 10 ) ? j + 0x30 : j + 0x57;
*q =(char)j;
q +=1 ;
}
// terminate the text string with NULL
*q = '\0';
return p;
}
unsigned short MakeID( char* id, int l )
{
unsigned short hash;
int i;
hash = 0xFFFF;
for ( i = 0 ; i < l ; i++ )
{
hash = ( ( hash & 0xFF00 ) >> NUM_BITS ) ^ ( crc_16_ccitt_table[( hash & 0x00FF ) ^ tolower( id[i] )] );
}
return hash;
}
unsigned long CreateID( char* id, int l )
{
unsigned long hash;
int i;
hash = 0;
for ( i = 0; i < l; i++ )
{
hash = ( hash >> NUM_BITS ) ^ modded_crc_16_ibm_table[( hash & 0x000000FF ) ^ tolower( id[i] )];
}
// Special processing to come up with the finalized hashcode
hash = ( hash >> 24 ) | ( ( hash >> 8 ) & 0x0000FF00 ) | ( ( hash << 8 ) & 0x00FF0000 ) | ( hash << 24 );
hash = ( hash >> 2 ) | 0x80000000;
return hash;
}
unsigned long MakeLocationID( char* id, char* room, int l )
{
unsigned long hash;
char* p;
char* q;
// Hash the base name
hash = CreateID( id, l );
// Change the hex of the hash into a string
p = HexToString( hash );
// Concatenate base hash string with "_<room nickname="">"
q = malloc( strlen( p ) + strlen( room ) + 2 ); // +2 for "_" and NULL
if ( q == NULL )
{
fprintf( stderr, "Error: malloc() failed.\r\n" );
exit( MALLOC_FAILED );
}
strcpy( q, p );
strcat( q, "_" );
strcat( q, room );
// Hash the newly created string
hash = CreateID( q, strlen( q ) );
free( p );
free( q );
return hash;
}
int main( int argc, char* argv[] )
{
int i;
unsigned int j, k, crc16ccitt_init, crc16ibm_init, hashmode, expecting_argument;
char* p;
char* q;
char* r;
unsigned short x;
unsigned long y;
if ( argc < 2 )
{
fprintf( stderr, "Error: wrong number of arguments.\r\n" );
usage( argv[0], 0 );
exit( WRONG_NUMBER_ARGUMENT );
}
// LUTs not initialized yet
crc16ibm_init = 0;
crc16ccitt_init = 0;
// assume hashing normal freelancer entities
hashmode = 0;
expecting_argument = 0;
for ( i = 1 ; i < argc ; i++ )
{
p = argv[i];
if ( *p == '-' )
{
p += 1;
switch ( *p )
{
case 'E':
case 'e':
if ( !crc16ibm_init )
{
Initialize_Modded_CRC_16_IBM_Table();
crc16ibm_init = 1;
}
hashmode = 0;
break;
case 'F':
case 'f':
if ( !crc16ccitt_init )
{
Initialize_CRC_16_CCITT_Table();
crc16ccitt_init = 1;
}
hashmode = 1;
break;
case 'H':
case 'h':
fprintf( stdout, "%s help:\r\n", argv[0] );
usage( argv[0], 1 );
return ( SUCCESSFUL_RUN );
break;
case 'L':
case 'l':
if ( !crc16ibm_init )
{
Initialize_Modded_CRC_16_IBM_Table();
crc16ibm_init = 1;
}
hashmode = 2;
break;
default:
fprintf( stderr, "Error: invalid flag \"%s\".\r\n", argv[i] );
usage( argv[0], 0 );
exit( INVALID_ARGUMENT );
break;
}
// Flag processed -- should now expect an argument
expecting_argument = 1;
}
else
{
switch ( hashmode )
{
case 0: // default hash algorithm (decoded by Sherlog)
if ( !crc16ibm_init ) // needed in just case no flags specified
{
Initialize_Modded_CRC_16_IBM_Table();
crc16ibm_init = 1;
}
y = CreateID( p, strlen( p ) );
fprintf( stdout, "\"%s\", %u, %d, 0x%.8X\r\n", p, y, y, y );
break;
case 1: // Faction ID hash algorithm
x = MakeID( p, strlen( p ) );
fprintf( stdout, "\"%s\", %u, %d, 0x%.4X\r\n", p, x, x, x );
break;
case 2: // Location ID hash algorithm (really just the default called twice)
// Two strings separated by a comma. Split them up then
// send them to MakeLocationID
// Assume one comma, and locate it in argument
j = strcspn( p , "," );
if ( j == strlen( p ) )
{
fprintf( stderr, "Error: invalid argument.\r\n" );
usage( argv[0], 0 );
exit( INVALID_ARGUMENT );
}
// Parse out the 1st argument
q = malloc( j + 1 );
if ( q == NULL )
{
fprintf( stderr, "Error: malloc() failed.\r\n" );
exit( MALLOC_FAILED );
}
for ( x = 0 ; x < j ; x++ )
{
q[x] = p[x];
}
q[j] = '\0';
// Parse out the 2nd argument
k = strlen( p ) - j - 1;
r = malloc( k + 1 );
if ( r == NULL )
{
fprintf( stderr, "Error: malloc() failed.\r\n" );
free( q );
exit( MALLOC_FAILED );
}
for ( x = 0 ; x < k ; x++ )
{
r[x] = p[j + x + 1];
}
r[k] = '\0';
// Arguments parsed. Now invoke the function
// and return the results.
y = MakeLocationID(q, r, strlen ( q ) );
fprintf( stdout, "\"%s\", %u, %d, 0x%.8X\r\n", p, y, y, y );
free ( q );
free ( r );
break;
default:
fprintf( stderr, "Error: unknown error." );
exit( UNKNOWN_ERROR );
break;
}
// No longer expecting an argument, though there
// is possibly more.
expecting_argument = 0;
}
}
// If we are still expecting an argument, this means user did
// not supply text after specifying a flag. In other words, the
// wrong number of arguments was given.
if ( expecting_argument )
{
fprintf( stderr, "Error: wrong number of arguments.\r\n" );
usage( argv[0], 0 );
exit( WRONG_NUMBER_ARGUMENT );
}
return ( SUCCESSFUL_RUN );
}
Edit: Forgot the code formatting brackets...silly me. Thanks, M0tah[/i][/i][/i][/i]</room></nickname_of_room></nickname_of_base></errno.h></string.h></stdlib.h></stdio.h>
Hi all,
Most of you should be familiar with the FLHash command line tool that was created long ago, based on the hashing algorithm decoded by Sherlog in 2003. While very useful, FLHash unfortunately cannot hash Freelancer faction IDs and Freelancer Location IDs (both used in the save game files). There are other programs out there that can do this, but there are still those of us who are using the old tool…
Therefore, I’ve taken the liberty of re-writing the code from ground up and extending the functionality of the tool so that it can now process all three types of hashes. For instructions, simply enter the program name by itself or enter it with the ‘-h’ option. Once again, I’ve included the source code with the executable so that people can know it’s safe. Credits go to Sherlog for figuring out the original algorithm.
I’ll upload the tool as soon as the Downloads section on this site has stabilized.
Cheers,
– The Haen.
adoxa wrote:
Having decoded the faction hash algorithm, I would’ve thought you’d know how it generates the location hash, too.
Oh, I know how to generate it all right. I apologize if I conveyed otherwise.
Like you wrote, it hashes the base name, prepends the hex as a string plus the underscore to the room name, and then hashes the new string. I have been manually determining location hashes using this process via the original FLHash.exe tool. I simply thought of the entire process as calling on the default hash algorithm twice, which was what I meant as “applied twice over”.
On another note, I spotted a minor error with FLFacHash’s output formatting. I’ve corrected the error and will re-upload once the problems with the Downloads are gone.
– The Haen.
adoxa wrote:
That’s right. I don’t believe so.
Ah, I see. Thanks for your answer.
I always thought of location hashes as FLHash being applied twice over. I didn’t know that Freelancer actually called a different routine just for location hashes. Though on further reflection, I guess I should have anticipated that.
At any rate, now that I’ve got these three hashes, that gives me an idea with which I can play around…
– The Haen.
Haen - now I KNOW you’re an old bugger like me!!
I’m not THAT old. In fact, believe or not, I’m not even an programmer – I’m a business major!
A quick question for Jason: I just noticed you mentioned “location hashes” for your createid program. Are you referring to the hash number after the “location =” entry in the save game files? If yes, do you know if they are used anywhere else?
Cheers,
– The Haen.
Why avoid CMD if you can do it all in practically 1 line using DOS’s “for” command?
Suppose you have FLFacHash and your input file with a list of IDs to be hashed (say, Input.txt) in the same directory. Then just open up the command prompt and enter the following commands:
echo off
for /F %i in (Input.txt) do FLFacHash %i >> Output.txt
echo on
Open up Output.txt and you should have the information you need in almost the format you wanted. If you are hashing normal hashes, then you can use FLHash instead of FLFacHash.
– The Haen.
StarTrader wrote:
Apart from universe.ini in [Base] entries (W02Fxx) I haven’t needed a hashcode for factions until now, where are they given/used?
As far as I know, faction hash codes are used exclusively in the save game files.
If you decode any of the save game files, you’ll notice right away that there’s a massive section of the file in this format: “visit = (some very big number here), (much smaller number here)”. Most of the bigger numbers are in the 2 - 3 billion range – these would be the hash codes for most Freelancer entities.
However, near the beginning of the visit section, you’ll also notice a bunch of visit lines in which the big number is much smaller – smaller than 65536, in fact. These are the faction ID hash codes. If a faction’s hash code is present in the save file indicates then that faction should show up in the player’s reputation list. Otherwise, it won’t.
How is this useful? Well, for example, in the vanilla game the Order faction will never show up in your faction reputation list thanks to some stuff hard-coded into the Freelancer main executable. If you’re not into exe hacking, one way you can get it to show up is to generate the Faction hash code for the Order (fc_or_grp => 3775), and stuff the line “visit=3775, 65” at the appropriate place in the save file.
Cannon wrote:
Wonderful, I’ve been wondering about the algorithm for ages but never found the time, motivation or talent to figure it out. Very nice.
I know. Actually, that’s how I found out that no algorithm for hashing faction IDs was out there in the first place. I downloaded your Discovery Account Manager (very nice tool, btw) and browsed through the source code, and saw your comments about how hashing faction IDs was still unknown. Seeing that I had solved that nearly 3 years ago and was rather selfishly not sharing, I decided to do the right thing, extract the snippet of code needed, re-compile a new executable in the grand ol’ tradition of command-line based FLHash.exe, and make it public. Probably should’ve done it back then – just surprised that no one else had bothered to decode the algorithm since that time.
Anyway, I hope this would be of use to you in your Discovery Account Manager.
Cheers,
– The Haen.
For the curious who wanted to see the source code right away, here it is:
/*
Name: FLFacHash
Author: Haenlomal
Date: October 2006
Purpose: Returns hashcodes for Freelancer Faction ID strings
This is free software. Permission to copy, store and use granted as long
as this copyright note remains intact.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#define _VERSION_STAMP 1.00.10
// The hash table used is the standard CRC-16-CCITT Lookup table using the standard big-endian
// polynomial of 0x1021.
#define POLYNOMIAL 0x1021
#define NUM_BITS 8
#define HASH_TABLE_SIZE (1 << NUM_BITS)
unsigned short FactionIDHashTable[HASH_TABLE_SIZE];
void usage( const char* prog )
{
fprintf( stderr, "Usage: %s faction_id [faction_id [faction_id [faction_id]]] ...\r\n", prog );
fprintf( stderr, "The faction hash code(s) will be returned in unsigned, signed, and hexadecimal\r\n" );
fprintf( stderr, "formats. The unsigned format is suitable for most purposes.\r\n" );
fprintf( stderr, "\r\n" );
fprintf( stderr, "Examples:\r\n" );
fprintf( stderr, "\r\n" );
fprintf( stderr, "\"%s fc_c_grp\"\r\n", prog );
fprintf( stderr, "This will return the faction hash code for faction fc_c_grp.\r\n" );
fprintf( stderr, "\r\n" );
fprintf( stderr, "\"%s li_p_grp ku_p_grp gd_bh_grp\"\r\n", prog );
fprintf( stderr, "This will return the faction hash codes for li_p_grp, ku_p_grp, and gd_bh_grp.\r\n" );
}
void InitializeHashTable()
{
int i, j;
unsigned short x;
for ( i = 0 ; i < HASH_TABLE_SIZE ; i++ )
{
x = i << ( 16 - NUM_BITS );
for ( j = 0; j < NUM_BITS ; j++)
{
x = ( x & 0x8000 ) ? ( x << 1 ) ^ POLYNOMIAL : ( x << 1 );
}
FactionIDHashTable[i] = x;
}
}
unsigned short MakeID( char* id, int l )
{
unsigned short hash, x;
int i;
hash = 0xFFFF;
for ( i = 0 ; i < l ; i++ )
{
x = ( hash & 0xFF00 ) >> 8;
hash = x ^ ( FactionIDHashTable[( hash & 0x00FF ) ^ tolower( id[i] )] );
}
return hash;
}
int main( int argc, char* argv[] )
{
int i;
char* p;
unsigned short x;
if ( argc < 2 )
{
fprintf( stderr, "Error: wrong number of arguments.\r\n" );
usage( argv[0] );
exit( 1 );
}
InitializeHashTable();
for ( i = 1 ; i < argc ; i++ )
{
p = argv[i];
x = MakeID( p, strlen ( p ) );
fprintf( stdout, "\"%s\", %u, %d, 0x%.4X\r\n", p, x, x, x );
}
return ( 0 );
}
Edit: Updated with latest version of the source code[/i][/i]</errno.h></string.h></stdlib.h></stdio.h>
Hi all,
Some of you may remember me from the very old TLR forums. I’ve recently returned to Freelancer, after noticing that there are quite a few interesting mods out there. Anyway, enough introduction – onto the reason for this post.
As some of you know, FLHash is a very basic program that produces hash codes for any given Freelancer ID. Unfortunately, it doesn’t seem to work for faction IDs – a different hash algorithm is used for that, and as far as I know, short of hooking directly into Content.dll no one really knows the exact method. Several years ago, I’ve searched the forums of several different Freelancer sites but came up empty.
So, I decided to get to work myself, and took about a day to solve the problem with my own little code, which I’ve been using ever since. I figured that since then, someone would have solved the problem, but apparently not. Once again, I searched for it, but came up empty.
So permit me to introduce FLFacHash, a tool that will generate a hashcode for any given Freelancer faction ID. FLFacHash is a standalone C executable, and in case anyone is worried, I’ve included the cleaned up C source code. It should be in the Files section shortly, assuming the mods approve of it. While the executable itself may be of little interest, maybe the source code may be of more use.
Cheers,
– The Haen.