2020-02-26 18:35:22 +01:00
import std.stdio ;
import std.csv ;
import std.algorithm ;
import std.typecons ;
import etc.c.sqlite3 ;
2020-02-27 14:31:48 +01:00
import std.string : toStringz , fromStringz , empty , strip , split ;
2020-02-26 18:35:22 +01:00
import std.exception ;
import std.conv : to ;
private class SQLiteException : Exception {
this ( int code , string msg1 , string msg2 , string file = __FILE__ , size_t line = __LINE__ ) {
super ( "SQLite3 error " ~ to ! string ( code ) ~ ( msg1 . empty ? "" : " " ) ~ msg1 ~ ": " ~ msg , file , line ) ;
}
}
extern ( C ) private int callback ( void * /*NotUsed*/ , int /*argc*/ , char * * /*argv*/ , char * * /*azColName*/ ) {
return 0 ;
}
2020-02-27 14:31:48 +01:00
private void insert_into_sqlite ( sqlite3 * db , string path ) {
2020-02-26 18:35:22 +01:00
auto file = File ( path , "r" ) ;
sqlite3_stmt * statement ;
scope ( exit ) { sqlite3_finalize ( statement ) ; }
{
2020-02-27 14:31:48 +01:00
string sql = "INSERT INTO GameList(Title, Edition, Version, Languages, Serial, Region) VALUES(?, ?, ?, ?, ?, ?);" ;
2020-02-26 18:35:22 +01:00
const int prepare_ret = sqlite3_prepare_v2 ( db , sql . ptr , cast ( int ) sql . length , & statement , null ) ;
if ( prepare_ret )
throw new SQLiteException ( prepare_ret , "" , sqlite3_errstr ( prepare_ret ) . fromStringz . idup ) ;
}
2020-02-27 14:31:48 +01:00
bool [ string ] serials_seen ;
// Expected CSV format: title,edition,version,languages,serial,region
foreach ( record ; file . byLine . joiner ( "\n" ) . csvReader ! ( Tuple ! ( string , string , string , string , string , string ) ) ) {
const int ver = cast ( int ) ( ( record [ 2 ] . empty ? 0.0 : to ! double ( record [ 2 ] ) ) * 100.0 ) ;
string [ ] serials = record [ 4 ] . split ( ";" ) ;
2020-02-27 14:37:05 +01:00
string languages = record [ 3 ] ;
string edition = record [ 1 ] ;
string title = record [ 0 ] ;
string region = record [ 5 ] ;
const int region_id = 0 ;
2020-02-27 14:31:48 +01:00
foreach ( serial ; serials ) {
serial = serial . strip ;
if ( serial in serials_seen ) {
continue ;
}
2020-02-26 18:35:22 +01:00
2020-02-27 14:31:48 +01:00
serials_seen [ serial ] = false ;
2020-02-27 14:37:05 +01:00
sqlite3_bind_text ( statement , 1 , title . ptr , cast ( int ) title . length , SQLITE_STATIC ) ; //title
sqlite3_bind_text ( statement , 2 , edition . ptr , cast ( int ) edition . length , SQLITE_STATIC ) ; //edition
2020-02-27 14:31:48 +01:00
sqlite3_bind_double ( statement , 3 , ver ) ; //version
2020-02-27 14:37:05 +01:00
if ( ! languages . empty )
sqlite3_bind_text ( statement , 4 , languages . ptr , cast ( int ) languages . length , SQLITE_STATIC ) ; //languages
else
sqlite3_bind_null ( statement , 4 ) ;
2020-02-27 14:31:48 +01:00
sqlite3_bind_text ( statement , 5 , serial . ptr , cast ( int ) serial . length , SQLITE_STATIC ) ; //serial
2020-02-27 14:37:05 +01:00
sqlite3_bind_int ( statement , 6 , region_id ) ; //region
stderr . writefln ( "insert into: \"%s\", \"%s\", \"%d\", \"%s\", \"%s\", \"%d\"" , record [ 0 ] , record [ 1 ] , ver , record [ 3 ] , serial , region_id ) ;
2020-02-27 14:31:48 +01:00
const int step_ret = sqlite3_step ( statement ) ;
if ( step_ret & & SQLITE_DONE ! = step_ret )
throw new SQLiteException ( step_ret , "" , sqlite3_errstr ( step_ret ) . fromStringz . idup ) ;
sqlite3_reset ( statement ) ;
}
2020-02-26 18:35:22 +01:00
}
}
int main ( string [ ] argv ) {
//input parameters should be csv files in this order: pal, ntsc_j, ntsc_u
2020-02-27 14:31:48 +01:00
stderr . writefln ( "got %s params: \"%s\", \"%s\"" , argv . length , argv [ 1 ] , argv [ 2 ] ) ;
2020-02-26 18:35:22 +01:00
2020-02-27 14:31:48 +01:00
if ( argv . length ! = 3 ) {
stderr . writefln ( "Expected 2 parameters: <input.csv> <output.sqlite3>, but %s were received" , argv . length - 1 ) ;
2020-02-26 18:35:22 +01:00
return 2 ;
}
try {
sqlite3 * db = null ;
scope ( exit ) { if ( db ) { sqlite3_close ( db ) ; } }
{
2020-02-27 14:31:48 +01:00
const int open_ret = sqlite3_open_v2 ( argv [ $ - 1 ] . toStringz ( ) , & db , SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE , null ) ;
2020-02-26 18:35:22 +01:00
if ( open_ret )
2020-02-27 14:31:48 +01:00
throw new SQLiteException ( open_ret , "opening database \"" ~ argv [ $ - 1 ] ~ "\"" , sqlite3_errmsg ( db ) . fromStringz . idup ) ;
2020-02-26 18:35:22 +01:00
}
{
2020-02-27 14:31:48 +01:00
string sql = "DROP TABLE IF EXISTS GameList; CREATE TABLE GameList(Serial TEXT PRIMARY KEY NOT NULL, Title TEXT NOT NULL, Edition TEXT, Version INT NOT NULL, Languages TEXT, Region TINYINT NOT NULL);" ;
2020-02-26 18:35:22 +01:00
char * error_msg = null ;
const int query_ret = sqlite3_exec ( db , sql . toStringz ( ) , & callback , null , & error_msg ) ;
if ( query_ret )
throw new SQLiteException ( query_ret , "creating GameList table" , error_msg . fromStringz . idup ) ;
}
2020-02-27 14:31:48 +01:00
insert_into_sqlite ( db , argv [ 1 ] ) ;
2020-02-26 18:35:22 +01:00
}
catch ( Exception err ) {
stderr . writeln ( err ) ;
return 1 ;
}
return 0 ;
}