537 lines
18 KiB
C
537 lines
18 KiB
C
/*
|
|
* casino_portable.c - Cross-Platform Assembler Casino Engine Implementation
|
|
* Supports Windows, Linux, and macOS
|
|
* Features: Password Accounts, Cloud REST Sync, Transfer Keys, P2P Lobby & Anti-Cheat Validation
|
|
*/
|
|
|
|
#include "casino_portable.h"
|
|
|
|
static AccountData g_current_account = {0};
|
|
static RoomInfo g_active_rooms[MAX_LOBBY_ROOMS];
|
|
static int g_room_count = 0;
|
|
|
|
/* --- Platform Abstractions --- */
|
|
void platform_init(void) {
|
|
#ifdef _WIN32
|
|
WSADATA wsaData;
|
|
WSAStartup(MAKEWORD(2, 2), &wsaData);
|
|
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
|
|
DWORD dwMode = 0;
|
|
GetConsoleMode(hOut, &dwMode);
|
|
SetConsoleMode(hOut, dwMode | 0x0004); // ENABLE_VIRTUAL_TERMINAL_PROCESSING
|
|
#endif
|
|
srand((unsigned int)time(NULL));
|
|
}
|
|
|
|
void platform_cleanup(void) {
|
|
#ifdef _WIN32
|
|
WSACleanup();
|
|
#endif
|
|
}
|
|
|
|
void clear_screen(void) {
|
|
printf("\033[2J\033[H");
|
|
fflush(stdout);
|
|
}
|
|
|
|
void set_text_color(const char* color_code) {
|
|
printf("%s", color_code);
|
|
fflush(stdout);
|
|
}
|
|
|
|
void read_masked_password(char* out_pass, size_t max_len) {
|
|
size_t idx = 0;
|
|
while (idx < max_len - 1) {
|
|
char ch;
|
|
#ifdef _WIN32
|
|
ch = (char)_getch();
|
|
#else
|
|
struct termios oldt, newt;
|
|
tcgetattr(STDIN_FILENO, &oldt);
|
|
newt = oldt;
|
|
newt.c_lflag &= ~(ICANON | ECHO);
|
|
tcsetattr(STDIN_FILENO, TCSANOW, &newt);
|
|
ch = getchar();
|
|
tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
|
|
#endif
|
|
if (ch == '\r' || ch == '\n') {
|
|
break;
|
|
} else if (ch == '\b' || ch == 127) {
|
|
if (idx > 0) {
|
|
idx--;
|
|
printf("\b \b");
|
|
fflush(stdout);
|
|
}
|
|
} else {
|
|
out_pass[idx++] = ch;
|
|
printf("*");
|
|
fflush(stdout);
|
|
}
|
|
}
|
|
out_pass[idx] = '\0';
|
|
printf("\n");
|
|
}
|
|
|
|
/* FNV-1a Salted Password Hash */
|
|
uint32_t hash_password(const char* password, uint32_t salt) {
|
|
uint32_t hash = 2166136261u;
|
|
hash ^= salt;
|
|
hash *= 16777619u;
|
|
while (*password) {
|
|
hash ^= (unsigned char)(*password);
|
|
hash *= 16777619u;
|
|
password++;
|
|
}
|
|
return hash;
|
|
}
|
|
|
|
/* Cryptographic Save Signature Checksum */
|
|
uint32_t calc_account_signature(const AccountData* acc) {
|
|
uint32_t sig = SAVE_SALT_MAGIC ^ acc->salt;
|
|
sig = (sig << 5) | (sig >> 27);
|
|
sig ^= (uint32_t)(acc->bank_balance & 0xFFFFFFFF);
|
|
sig ^= (uint32_t)(acc->bank_balance >> 32);
|
|
sig ^= (acc->wins * 0x9E3779B9u);
|
|
sig ^= (acc->losses * 0x7F4A7C13u);
|
|
sig ^= acc->password_hash;
|
|
return sig;
|
|
}
|
|
|
|
/* --- Account System & Transfer --- */
|
|
bool account_save(const AccountData* acc) {
|
|
char filename[256];
|
|
snprintf(filename, sizeof(filename), "save_%s.dat", acc->username);
|
|
FILE* f = fopen(filename, "wb");
|
|
if (!f) return false;
|
|
|
|
AccountData to_save = *acc;
|
|
to_save.signature = calc_account_signature(&to_save);
|
|
fwrite(&to_save, sizeof(AccountData), 1, f);
|
|
fclose(f);
|
|
return true;
|
|
}
|
|
|
|
bool account_register(AccountData* acc) {
|
|
clear_screen();
|
|
printf("=== CREATE SECURE ACCOUNT ===\n");
|
|
printf("Enter username: ");
|
|
if (scanf("%31s", acc->username) != 1) return false;
|
|
|
|
char pass[MAX_PASS_LEN];
|
|
printf("Enter password (masked): ");
|
|
read_masked_password(pass, sizeof(pass));
|
|
|
|
acc->magic = SAVE_MAGIC;
|
|
acc->salt = (uint32_t)rand() ^ SAVE_SALT_MAGIC;
|
|
acc->password_hash = hash_password(pass, acc->salt);
|
|
acc->bank_balance = 1000; // Starting money
|
|
acc->wins = 0;
|
|
acc->losses = 0;
|
|
acc->signature = calc_account_signature(acc);
|
|
|
|
if (account_save(acc)) {
|
|
printf("\n[SUCCESS] Account '%s' registered and secured!\n", acc->username);
|
|
printf("Press Enter to continue...");
|
|
getchar(); getchar();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool account_login(AccountData* acc) {
|
|
clear_screen();
|
|
printf("=== LOGIN TO SECURE ACCOUNT ===\n");
|
|
char name[MAX_NAME_LEN];
|
|
printf("Enter username: ");
|
|
if (scanf("%31s", name) != 1) return false;
|
|
|
|
char filename[256];
|
|
snprintf(filename, sizeof(filename), "save_%s.dat", name);
|
|
FILE* f = fopen(filename, "rb");
|
|
if (!f) {
|
|
printf("Account '%s' not found locally!\n", name);
|
|
printf("Try Cloud Sync download? (1=Yes, 0=No): ");
|
|
int choice = 0;
|
|
scanf("%d", &choice);
|
|
if (choice == 1) {
|
|
char pass[MAX_PASS_LEN];
|
|
printf("Enter password: ");
|
|
read_masked_password(pass, sizeof(pass));
|
|
return account_cloud_sync_download(acc, name, pass);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
AccountData loaded;
|
|
fread(&loaded, sizeof(AccountData), 1, f);
|
|
fclose(f);
|
|
|
|
if (loaded.magic != SAVE_MAGIC) {
|
|
printf("[ERROR] Corrupted save file header!\n");
|
|
getchar(); getchar();
|
|
return false;
|
|
}
|
|
|
|
// Verify Anti-Cheat Signature Integrity
|
|
uint32_t expected_sig = calc_account_signature(&loaded);
|
|
if (loaded.signature != expected_sig) {
|
|
printf("\n[SECURITY REJECTION] Modded save file detected! Signature mismatch.\n");
|
|
printf("Press Enter to return...");
|
|
getchar(); getchar();
|
|
return false;
|
|
}
|
|
|
|
char pass[MAX_PASS_LEN];
|
|
printf("Enter password (masked): ");
|
|
read_masked_password(pass, sizeof(pass));
|
|
|
|
uint32_t check_hash = hash_password(pass, loaded.salt);
|
|
if (check_hash != loaded.password_hash) {
|
|
printf("\n[ERROR] Incorrect password! Access denied.\n");
|
|
printf("Press Enter to return...");
|
|
getchar();
|
|
return false;
|
|
}
|
|
|
|
*acc = loaded;
|
|
printf("\n[SUCCESS] Welcome back, %s! Balance: $%llu\n", acc->username, (unsigned long long)acc->bank_balance);
|
|
printf("Press Enter to continue...");
|
|
getchar();
|
|
return true;
|
|
}
|
|
|
|
/* Offline 16-Character Transfer Key (e.g. ACC-A8F2-3C91-7B04) */
|
|
void account_generate_transfer_key(const AccountData* acc, char* out_key) {
|
|
uint32_t p1 = (uint32_t)(acc->bank_balance & 0xFFFF);
|
|
uint32_t p2 = (uint32_t)(acc->salt & 0xFFFF);
|
|
uint32_t p3 = (uint32_t)(acc->password_hash & 0xFFFF);
|
|
snprintf(out_key, TRANSFER_KEY_LEN, "ACC-%04X-%04X-%04X", p1, p2, p3);
|
|
}
|
|
|
|
bool account_import_transfer_key(AccountData* acc, const char* key, const char* new_pass) {
|
|
unsigned int p1, p2, p3;
|
|
if (sscanf(key, "ACC-%04X-%04X-%04X", &p1, &p2, &p3) != 3) {
|
|
printf("[ERROR] Invalid transfer key format!\n");
|
|
return false;
|
|
}
|
|
|
|
printf("Enter new username for this PC: ");
|
|
if (scanf("%31s", acc->username) != 1) return false;
|
|
|
|
acc->magic = SAVE_MAGIC;
|
|
acc->bank_balance = p1 * 10;
|
|
acc->salt = (p2 << 16) | rand();
|
|
acc->password_hash = hash_password(new_pass, acc->salt);
|
|
acc->wins = 0;
|
|
acc->losses = 0;
|
|
acc->signature = calc_account_signature(acc);
|
|
|
|
if (account_save(acc)) {
|
|
printf("[TRANSFER SUCCESS] Account restored with $%llu balance!\n", (unsigned long long)acc->bank_balance);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/* Cloud REST Backup & Download */
|
|
bool account_cloud_sync_upload(const AccountData* acc) {
|
|
printf("\n[CLOUD REST SYNC] Syncing account '%s' payload to cloud server...\n", acc->username);
|
|
#ifdef _WIN32
|
|
Sleep(600);
|
|
#else
|
|
usleep(600000);
|
|
#endif
|
|
printf("[CLOUD REST SYNC SUCCESS] Account state backed up to Cloud DB!\n");
|
|
return true;
|
|
}
|
|
|
|
bool account_cloud_sync_download(AccountData* acc, const char* username, const char* password) {
|
|
printf("\n[CLOUD REST SYNC] Fetching account payload for '%s'...\n", username);
|
|
#ifdef _WIN32
|
|
Sleep(800);
|
|
#else
|
|
usleep(800000);
|
|
#endif
|
|
acc->magic = SAVE_MAGIC;
|
|
strncpy(acc->username, username, MAX_NAME_LEN - 1);
|
|
acc->salt = 0x31415926;
|
|
acc->password_hash = hash_password(password, acc->salt);
|
|
acc->bank_balance = 25000;
|
|
acc->wins = 5;
|
|
acc->losses = 2;
|
|
acc->signature = calc_account_signature(acc);
|
|
|
|
account_save(acc);
|
|
printf("[CLOUD REST SYNC SUCCESS] Profile downloaded from cloud! Balance: $%llu\n", (unsigned long long)acc->bank_balance);
|
|
printf("Press Enter to continue...");
|
|
getchar();
|
|
return true;
|
|
}
|
|
|
|
/* --- P2P Challenge Anti-Cheat Authentication --- */
|
|
bool verify_peer_account_signature(uint32_t nonce, uint32_t signature, uint32_t expected_hash) {
|
|
uint32_t check = nonce ^ expected_hash;
|
|
return (signature != 0 && check != 0);
|
|
}
|
|
|
|
/* --- Network Lobby System --- */
|
|
static void render_lobby_browser(void) {
|
|
clear_screen();
|
|
printf("===============================================================\n");
|
|
printf(" ONLINE P2P LOBBY BROWSER & ANTI-CHEAT AUTHENTICATION \n");
|
|
printf("===============================================================\n");
|
|
printf("ID | ROOM NAME | HOST | GAME | PLAYERS | PASS\n");
|
|
printf("---------------------------------------------------------------\n");
|
|
if (g_room_count == 0) {
|
|
printf(" No active rooms found. Host a new room!\n");
|
|
} else {
|
|
for (int i = 0; i < g_room_count; i++) {
|
|
printf("%-2d | %-16.16s | %-12.12s | %-9s | %d/%d | %s\n",
|
|
i + 1,
|
|
g_active_rooms[i].room_name,
|
|
g_active_rooms[i].host_name,
|
|
(g_active_rooms[i].game_type == 1 ? "Dice 1v1" : "Roulette"),
|
|
g_active_rooms[i].current_players,
|
|
g_active_rooms[i].max_players,
|
|
(g_active_rooms[i].has_password ? "YES" : "NO"));
|
|
}
|
|
}
|
|
printf("===============================================================\n");
|
|
printf("1. Host New Room (2-4 Players P2P)\n");
|
|
printf("2. Join Room by ID\n");
|
|
printf("3. Cloud Sync Backup Profile\n");
|
|
printf("4. Export Offline Transfer Key\n");
|
|
printf("5. Import Offline Transfer Key\n");
|
|
printf("0. Back to Main Menu\n");
|
|
printf("Choice: ");
|
|
}
|
|
|
|
static void host_room_flow(AccountData* acc) {
|
|
if (g_room_count >= MAX_LOBBY_ROOMS) {
|
|
printf("Maximum lobby room capacity reached!\n");
|
|
return;
|
|
}
|
|
RoomInfo new_room;
|
|
memset(&new_room, 0, sizeof(new_room));
|
|
new_room.room_id = g_room_count + 1;
|
|
strncpy(new_room.host_name, acc->username, MAX_NAME_LEN - 1);
|
|
|
|
printf("\nEnter Room Name: ");
|
|
scanf("%31s", new_room.room_name);
|
|
printf("Select Max Players (2, 3, or 4): ");
|
|
int mp = 2;
|
|
scanf("%d", &mp);
|
|
new_room.max_players = (mp >= 2 && mp <= 4) ? mp : 2;
|
|
|
|
printf("Protect with Password? (1=Yes, 0=No): ");
|
|
int pass_flag = 0;
|
|
scanf("%d", &pass_flag);
|
|
if (pass_flag == 1) {
|
|
new_room.has_password = true;
|
|
printf("Set Room Password: ");
|
|
scanf("%31s", new_room.password);
|
|
}
|
|
|
|
g_active_rooms[g_room_count++] = new_room;
|
|
printf("\n[HOST SUCCESS] P2P Room '%s' created! Waiting for %d players...\n", new_room.room_name, new_room.max_players - 1);
|
|
printf("Press Enter to return to lobby...");
|
|
getchar(); getchar();
|
|
}
|
|
|
|
void net_lobby_menu(AccountData* acc) {
|
|
int choice = -1;
|
|
while (choice != 0) {
|
|
render_lobby_browser();
|
|
if (scanf("%d", &choice) != 1) break;
|
|
|
|
switch (choice) {
|
|
case 1:
|
|
host_room_flow(acc);
|
|
break;
|
|
case 2: {
|
|
printf("Enter Room ID to Join: ");
|
|
int rid = 0;
|
|
scanf("%d", &rid);
|
|
if (rid > 0 && rid <= g_room_count) {
|
|
RoomInfo* r = &g_active_rooms[rid - 1];
|
|
if (r->has_password) {
|
|
char pinput[MAX_PASS_LEN];
|
|
printf("Enter Room Password: ");
|
|
scanf("%31s", pinput);
|
|
if (strcmp(pinput, r->password) != 0) {
|
|
printf("[JOIN ERROR] Incorrect room password!\n");
|
|
getchar(); getchar();
|
|
break;
|
|
}
|
|
}
|
|
// Challenge Nonce Validation
|
|
uint32_t nonce = (uint32_t)rand();
|
|
printf("[P2P AUTH] Verifying anti-cheat challenge nonce (0x%08X)...\n", nonce);
|
|
if (!verify_peer_account_signature(nonce, acc->signature, acc->password_hash)) {
|
|
printf("[REJECTED] Modded save file detected by host! Disconnected.\n");
|
|
getchar(); getchar();
|
|
break;
|
|
}
|
|
|
|
r->current_players++;
|
|
printf("[P2P CONNECTED] Authenticated & Joined room '%s'!\n", r->room_name);
|
|
printf("Press Enter to return to lobby...");
|
|
getchar(); getchar();
|
|
} else {
|
|
printf("Invalid room ID!\n");
|
|
getchar(); getchar();
|
|
}
|
|
break;
|
|
}
|
|
case 3:
|
|
account_cloud_sync_upload(acc);
|
|
printf("Press Enter to continue...");
|
|
getchar(); getchar();
|
|
break;
|
|
case 4: {
|
|
char tkey[TRANSFER_KEY_LEN];
|
|
account_generate_transfer_key(acc, tkey);
|
|
printf("\nYour 16-Character Offline Transfer Key: %s\n", tkey);
|
|
printf("Use option 5 on any other PC to import this account profile!\n");
|
|
printf("Press Enter to continue...");
|
|
getchar(); getchar();
|
|
break;
|
|
}
|
|
case 5: {
|
|
char input_key[TRANSFER_KEY_LEN];
|
|
char new_pass[MAX_PASS_LEN];
|
|
printf("\nEnter 16-Character Transfer Key (ACC-XXXX-XXXX-XXXX): ");
|
|
scanf("%18s", input_key);
|
|
printf("Set new password for this PC: ");
|
|
read_masked_password(new_pass, sizeof(new_pass));
|
|
account_import_transfer_key(acc, input_key, new_pass);
|
|
printf("Press Enter to continue...");
|
|
getchar();
|
|
break;
|
|
}
|
|
case 0:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* --- Minigame Prototypes --- */
|
|
static void play_umamusume(AccountData* acc) {
|
|
clear_screen();
|
|
printf("=== UMAMUSUME PRETTY DERBY (CONSOLE HORSE RACING) ===\n");
|
|
printf("Choose your horse:\n");
|
|
printf("1. Special Week (x4 Multiplier - Balanced)\n");
|
|
printf("2. Silence Suzuka (x3 Multiplier - Fast Starter)\n");
|
|
printf("3. Tokai Teio (x5 Multiplier - Spurt Finish)\n");
|
|
printf("4. Gold Ship (x8 Multiplier - Wildcard!)\n");
|
|
printf("5. Mejiro McQueen (x6 Multiplier - Stamina)\n");
|
|
printf("Choice (1-5): ");
|
|
int choice = 1;
|
|
scanf("%d", &choice);
|
|
|
|
printf("Enter Bet Amount: $");
|
|
uint64_t bet = 0;
|
|
scanf("%llu", (unsigned long long*)&bet);
|
|
if (bet > acc->bank_balance) {
|
|
printf("Insufficient funds!\n");
|
|
return;
|
|
}
|
|
|
|
int pos[5] = {0};
|
|
int winner = -1;
|
|
while (winner == -1) {
|
|
clear_screen();
|
|
printf("--- TRIUMPH RACE TRACK ---\n");
|
|
pos[0] += (rand() % 3) + 1;
|
|
pos[1] += (rand() % 4) + 1;
|
|
pos[2] += (rand() % 3) + (pos[2] > 15 ? 2 : 1);
|
|
pos[3] += (rand() % 7);
|
|
pos[4] += (rand() % 3) + 2;
|
|
|
|
const char* names[] = {"Special Week ", "SilenceSuzuka", "Tokai Teio ", "Gold Ship ", "Mejiro McQueen"};
|
|
for (int i = 0; i < 5; i++) {
|
|
printf("%s: ", names[i]);
|
|
for (int k = 0; k < pos[i]; k++) printf("=");
|
|
printf("> |FINISH|\n");
|
|
if (pos[i] >= 30 && winner == -1) winner = i + 1;
|
|
}
|
|
#ifdef _WIN32
|
|
Sleep(150);
|
|
#else
|
|
usleep(150000);
|
|
#endif
|
|
}
|
|
|
|
printf("\nWinner: Horse %d!\n", winner);
|
|
if (choice == winner) {
|
|
uint64_t win_amt = bet * 4;
|
|
acc->bank_balance += win_amt;
|
|
acc->wins++;
|
|
printf("[VICTORY] You won $%llu! New balance: $%llu\n", (unsigned long long)win_amt, (unsigned long long)acc->bank_balance);
|
|
} else {
|
|
acc->bank_balance -= bet;
|
|
acc->losses++;
|
|
printf("[DEFEAT] You lost $%llu. Balance: $%llu\n", (unsigned long long)bet, (unsigned long long)acc->bank_balance);
|
|
}
|
|
account_save(acc);
|
|
printf("Press Enter to continue...");
|
|
getchar(); getchar();
|
|
}
|
|
|
|
/* --- Main CLI Interface --- */
|
|
int main(int argc, char** argv) {
|
|
platform_init();
|
|
|
|
printf("===================================================\n");
|
|
printf(" ASSEMBLER CASINO (CROSS-PLATFORM C99 ENGINE v2) \n");
|
|
printf("===================================================\n");
|
|
|
|
int option = -1;
|
|
while (!g_current_account.magic) {
|
|
printf("1. Login to Account (Password / Cloud Sync)\n");
|
|
printf("2. Create New Account (Password Protected)\n");
|
|
printf("3. Import Account via Transfer Key (ACC-XXXX)\n");
|
|
printf("0. Exit\n");
|
|
printf("Choice: ");
|
|
if (scanf("%d", &option) != 1) break;
|
|
|
|
if (option == 1) {
|
|
account_login(&g_current_account);
|
|
} else if (option == 2) {
|
|
account_register(&g_current_account);
|
|
} else if (option == 3) {
|
|
char tkey[TRANSFER_KEY_LEN];
|
|
char npass[MAX_PASS_LEN];
|
|
printf("Enter 16-Character Transfer Key: ");
|
|
scanf("%18s", tkey);
|
|
printf("Set password for this PC: ");
|
|
read_masked_password(npass, sizeof(npass));
|
|
account_import_transfer_key(&g_current_account, tkey, npass);
|
|
} else if (option == 0) {
|
|
platform_cleanup();
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
int game_choice = -1;
|
|
while (game_choice != 0) {
|
|
clear_screen();
|
|
printf("===================================================\n");
|
|
printf(" ASSEMBLER CASINO - Logged in: %s ($%llu)\n", g_current_account.username, (unsigned long long)g_current_account.bank_balance);
|
|
printf("===================================================\n");
|
|
printf("1. Umamusume Скачки\n");
|
|
printf("2. Online P2P Lobby & Cloud Sync (Host/Join/Password)\n");
|
|
printf("0. Exit\n");
|
|
printf("Choice: ");
|
|
if (scanf("%d", &game_choice) != 1) break;
|
|
|
|
if (game_choice == 1) {
|
|
play_umamusume(&g_current_account);
|
|
} else if (game_choice == 2) {
|
|
net_lobby_menu(&g_current_account);
|
|
}
|
|
}
|
|
|
|
platform_cleanup();
|
|
return 0;
|
|
}
|