import std.stdio; import std.csv; import std.algorithm; import std.typecons; import etc.c.sqlite3; import std.string; import std.exception; import std.conv : to; import std.path : baseName; 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; } enum Regions : int { Unknown, Europe, Japan, USA, Asia, Germany, Spain, Korea, All, Italy, France, Russia, Poland, Netherlands, Portugal, India, Belgium, UnitedKingdom, Australia }; private int region_id(string path) pure { switch (baseName(path, ".png")) { case "E": return Regions.Europe; case "J": return Regions.Japan; case "U": return Regions.USA; case "A": return Regions.Asia; case "G": return Regions.Germany; case "S": return Regions.Spain; case "K": return Regions.Korea; case "W": return Regions.All; case "I": return Regions.Italy; case "F": return Regions.France; case "R": return Regions.Russia; case "P": return Regions.Poland; case "N": return Regions.Netherlands; case "Pt": return Regions.Portugal; case "In": return Regions.India; case "Be": return Regions.Belgium; case "Uk": return Regions.UnitedKingdom; case "Au": return Regions.Australia; default: return Regions.Unknown; } } private void insert_into_sqlite(sqlite3* db, string path) { auto file = File(path, "r"); sqlite3_stmt* statement; scope(exit) {sqlite3_finalize(statement);} { string sql = "INSERT INTO GameList(Title, Edition, Version, Languages, Serial, Region) VALUES(?, ?, ?, ?, ?, ?);"; 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); } 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]; string languages = record[3]; string edition = record[1].splitter(";").map!strip.join(";"); string title = record[0]; string region = record[5]; foreach (serial; serials.splitter(";").map!strip) { if (serial in serials_seen) { continue; } serials_seen[serial] = false; 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 sqlite3_bind_double(statement, 3, ver); //version if (!languages.empty) sqlite3_bind_text(statement, 4, languages.ptr, cast(int)languages.length, SQLITE_STATIC); //languages else sqlite3_bind_null(statement, 4); sqlite3_bind_text(statement, 5, serial.ptr, cast(int)serial.length, SQLITE_STATIC); //serial sqlite3_bind_int(statement, 6, region_id(region)); //region //stderr.writefln("insert into: \"%s\", \"%s\", \"%d\", \"%s\", \"%s\", \"%d\", \"%s\"", title, edition, ver, languages, serial, region_id(region), region); 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); } } } int main(string[] argv) { //input parameters should be csv files in this order: pal, ntsc_j, ntsc_u stderr.writefln("got %s params: \"%s\", \"%s\"", argv.length, argv[1], argv[2]); if (argv.length != 3) { stderr.writefln("Expected 2 parameters: , but %s were received", argv.length - 1); return 2; } try { sqlite3* db = null; scope(exit) { if (db) {sqlite3_close(db);} } { const int open_ret = sqlite3_open_v2(argv[$-1].toStringz(), &db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, null); if (open_ret) throw new SQLiteException(open_ret, "opening database \"" ~ argv[$-1] ~ "\"", sqlite3_errmsg(db).fromStringz.idup); } { 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);"; 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); } insert_into_sqlite(db, argv[1]); } catch (Exception err) { stderr.writeln(err); return 1; } return 0; }