CfgUSBLoader/source/gc.c
2015-01-17 10:11:08 +00:00

740 lines
19 KiB
C

#include <gccore.h>
#include <stdio.h>
#include <sdcard/wiisd_io.h>
#include <unistd.h>
#include <sys/statvfs.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <malloc.h>
#include <fcntl.h>
#include <fat.h>
#include "gc.h"
#include "dir.h"
#include "util.h"
#include "gettext.h"
#include "wpad.h"
#include "debug.h"
#include "disc.h"
#include "fileOps.h"
#include "cfg.h"
#include "NintendontConfig.h"
#define MAX_FAT_PATH 1024
#define SRAM_ENGLISH 0
#define SRAM_GERMAN 1
#define SRAM_FRENCH 2
#define SRAM_SPANISH 3
#define SRAM_ITALIAN 4
#define SRAM_DUTCH 5
syssram* __SYS_LockSram();
u32 __SYS_UnlockSram(u32 write);
u32 __SYS_SyncSram(void);
extern char wbfs_fs_drive[16];
char dm_boot_drive[16];
void GC_SetVideoMode(u8 videomode, bool devo)
{
syssram *sram;
sram = __SYS_LockSram();
//void *m_frameBuf;
static GXRModeObj *rmode;
int memflag = 0;
if (devo) {
if((VIDEO_HaveComponentCable() && (CONF_GetProgressiveScan() > 0)) && videomode > 3)
sram->flags |= 0x80; //set progressive flag
else
sram->flags &= 0x7F; //clear progressive flag
if(videomode == 1 || videomode == 3 || videomode == 5)
{
memflag = 1;
sram->flags |= 0x01; // Set bit 0 to set the video mode to PAL
sram->ntd |= 0x40; //set pal60 flag
}
else
{
sram->flags &= 0xFE; // Clear bit 0 to set the video mode to NTSC
sram->ntd &= 0xBF; //clear pal60 flag
}
if(videomode == 1) {
rmode = &TVPal528IntDf;
} else if(videomode == 2) {
rmode = &TVNtsc480IntDf;
}
__SYS_UnlockSram(1); // 1 -> write changes
while(!__SYS_SyncSram());
/* Set video mode */
if (rmode != NULL)
VIDEO_Configure(rmode);
/* Setup video */
VIDEO_SetBlack(TRUE);
VIDEO_Flush();
VIDEO_WaitVSync();
VIDEO_WaitVSync();
return;
}
if((VIDEO_HaveComponentCable() && (CONF_GetProgressiveScan() > 0)) && videomode > 3)
sram->flags |= 0x80; //set progressive flag
else
sram->flags &= 0x7F; //clear progressive flag
if(videomode == 1 || videomode == 3 || videomode == 5)
{
memflag = 1;
sram->flags |= 0x01; // Set bit 0 to set the video mode to PAL
sram->ntd |= 0x40; //set pal60 flag
}
else
{
sram->flags &= 0xFE; // Clear bit 0 to set the video mode to NTSC
sram->ntd &= 0xBF; //clear pal60 flag
}
if(videomode == 1)
rmode = &TVPal528IntDf;
else if(videomode == 2)
rmode = &TVNtsc480IntDf;
else if(videomode == 3)
{
rmode = &TVEurgb60Hz480IntDf;
memflag = 5;
}
else if(videomode == 4)
rmode = &TVNtsc480Prog;
else if(videomode == 5)
{
rmode = &TVEurgb60Hz480Prog;
memflag = 5;
}
__SYS_UnlockSram(1); // 1 -> write changes
while(!__SYS_SyncSram());
/* Set video mode to PAL or NTSC */
*(vu32*)0x800000CC = memflag;
DCFlushRange((void *)(0x800000CC), 4);
//ICInvalidateRange((void *)(0x800000CC), 4);
if (rmode != 0)
VIDEO_Configure(rmode);
//m_frameBuf = MEM_K0_TO_K1(SYS_AllocateFramebuffer(rmode));
//VIDEO_ClearFrameBuffer(rmode, m_frameBuf, COLOR_BLACK);
//VIDEO_SetNextFramebuffer(m_frameBuf);
/* Setup video */
VIDEO_SetBlack(TRUE);
VIDEO_Flush();
VIDEO_WaitVSync();
if(rmode->viTVMode&VI_NON_INTERLACE)
VIDEO_WaitVSync();
}
u8 get_wii_language()
{
switch (CONF_GetLanguage())
{
case CONF_LANG_GERMAN:
return SRAM_GERMAN;
case CONF_LANG_FRENCH:
return SRAM_FRENCH;
case CONF_LANG_SPANISH:
return SRAM_SPANISH;
case CONF_LANG_ITALIAN:
return SRAM_ITALIAN;
case CONF_LANG_DUTCH:
return SRAM_DUTCH;
default:
return SRAM_ENGLISH;
}
}
void GC_SetLanguage(u8 lang)
{
if (lang == 0)
lang = get_wii_language();
else
lang--;
syssram *sram;
sram = __SYS_LockSram();
sram->lang = lang;
__SYS_UnlockSram(1); // 1 -> write changes
while(!__SYS_SyncSram());
}
s32 DML_RemoveGame(struct discHdr header)
{
fsop_deleteFolder(header.path);
return 0;
}
int DML_GameIsInstalled(char *folder)
{
int ret = 0;
char source[300];
snprintf(source, sizeof(source), "%s/game.iso", folder);
FILE *f = fopen(source, "rb");
if(f)
{
dbg_printf("Found on: %s\n", folder);
fclose(f);
ret = 1;
}
else
{
snprintf(source, sizeof(source), "%s/sys/boot.bin", folder);
f = fopen(source, "rb");
if(f)
{
dbg_printf("Found on: %s\n", folder);
fclose(f);
ret = 2;
}
}
return ret;
}
void DML_New_SetOptions(char *GamePath,char *CheatPath, char *NewCheatPath, bool cheats, bool debugger, u8 NMM, u8 nodisc, u8 LED, u8 DMLvideoMode, u8 W_SCREEN, u8 PHOOK, u8 V_PATCH, u8 screenshot)
{
dbg_printf("DML: Launch game '%s/game.iso' through memory (new method)\n", GamePath);
DML_CFG *DMLCfg = (DML_CFG*)memalign(32, sizeof(DML_CFG));
memset(DMLCfg, 0, sizeof(DML_CFG));
DMLCfg->Magicbytes = 0xD1050CF6;
if (CFG.dml >= CFG_DM_2_2)
DMLCfg->Version = 0x00000002;
else
DMLCfg->Version = 0x00000001;
if (V_PATCH == 1)
DMLCfg->VideoMode |= DML_VID_FORCE;
else if (V_PATCH == 2)
DMLCfg->VideoMode |= DML_VID_DML_AUTO;
else
DMLCfg->VideoMode |= DML_VID_NONE;
//DMLCfg->Config |= DML_CFG_ACTIVITY_LED; //Sorry but I like it lol, option will may follow
//DMLCfg->Config |= DML_CFG_PADHOOK; //Makes life easier, l+z+b+digital down...
if(GamePath != NULL)
{
if(DML_GameIsInstalled(GamePath) == 2)
snprintf(DMLCfg->GamePath, sizeof(DMLCfg->GamePath), "/games/%s/", get_DM_Game_Folder(GamePath));
else
snprintf(DMLCfg->GamePath, sizeof(DMLCfg->GamePath), "/games/%s/game.iso", get_DM_Game_Folder(GamePath));
DMLCfg->Config |= DML_CFG_GAME_PATH;
}
if(CheatPath != NULL && NewCheatPath != NULL && cheats)
{
char *ptr;
if(strstr(CheatPath, dm_boot_drive) == NULL)
{
fsop_CopyFile(CheatPath, NewCheatPath);
ptr = &NewCheatPath[strlen(dm_boot_drive)];
}
else
ptr = &CheatPath[strlen(dm_boot_drive)];
strncpy(DMLCfg->CheatPath, ptr, sizeof(DMLCfg->CheatPath));
DMLCfg->Config |= DML_CFG_CHEAT_PATH;
}
if(cheats)
{
DMLCfg->Config |= DML_CFG_CHEATS;
printf_x(gt("Ocarina: Searching codes..."));
printf("\n");
sleep(1);
}
if(debugger)
DMLCfg->Config |= DML_CFG_DEBUGGER;
if(NMM > 0)
DMLCfg->Config |= DML_CFG_NMM;
if(NMM > 1)
DMLCfg->Config |= DML_CFG_NMM_DEBUG;
if(nodisc > 0 && CFG.dml >= CFG_DM_2_1)
DMLCfg->Config |= DML_CFG_NODISC;
else if (nodisc > 0)
DMLCfg->Config |= DML_CFG_FORCE_WIDE;
if(LED > 0)
DMLCfg->Config |= DML_CFG_ACTIVITY_LED;
if(W_SCREEN > 0)
DMLCfg->Config |= DML_CFG_FORCE_WIDE;
if(PHOOK > 0)
DMLCfg->Config |= DML_CFG_PADHOOK;
if(screenshot > 0)
DMLCfg->Config |= DML_CFG_SCREENSHOT;
if(DMLvideoMode == 1 && V_PATCH == 1)
DMLCfg->VideoMode |= DML_VID_FORCE_PAL50;
else if(DMLvideoMode == 2 && V_PATCH == 1)
DMLCfg->VideoMode |= DML_VID_FORCE_NTSC;
else if(DMLvideoMode == 3 && V_PATCH == 1)
DMLCfg->VideoMode |= DML_VID_FORCE_PAL60;
else if(DMLvideoMode >= 4 && V_PATCH == 1)
DMLCfg->VideoMode |= DML_VID_FORCE_PROG;
if(DMLvideoMode > 3)
DMLCfg->VideoMode |= DML_VID_PROG_PATCH;
//Write options into memory
memcpy((void *)0x80001700, DMLCfg, sizeof(DML_CFG));
DCFlushRange((void *)(0x80001700), sizeof(DML_CFG));
//DML v1.2+
memcpy((void *)0x81200000, DMLCfg, sizeof(DML_CFG));
DCFlushRange((void *)(0x81200000), sizeof(DML_CFG));
free(DMLCfg);
}
void DML_Old_SetOptions(char *GamePath, char *CheatPath, char *NewCheatPath, bool cheats)
{
dbg_printf("DML: Launch game 'sd:/games/%s/game.iso' through boot.bin (old method)\n", GamePath);
FILE *f;
f = fopen("sd:/games/boot.bin", "wb");
fwrite(get_DM_Game_Folder(GamePath), 1, strlen(get_DM_Game_Folder(GamePath)) + 1, f);
fclose(f);
if(cheats && strstr(CheatPath, NewCheatPath) == NULL)
fsop_CopyFile(CheatPath, NewCheatPath);
//Tell DML to boot the game from sd card
*(vu32*)0x80001800 = 0xB002D105;
DCFlushRange((void *)(0x80001800), 4);
ICInvalidateRange((void *)(0x80001800), 4);
*(vu32*)0xCC003024 |= 7;
}
void DML_New_SetBootDiscOption(char *CheatPath, char *NewCheatPath, bool cheats, u8 NMM, u8 LED, u8 DMLvideoMode)
{
dbg_printf("Booting GC game\n");
DML_CFG *DMLCfg = (DML_CFG*)malloc(sizeof(DML_CFG));
memset(DMLCfg, 0, sizeof(DML_CFG));
DMLCfg->Magicbytes = 0xD1050CF6;
if (CFG.dml >= CFG_DM_2_2)
DMLCfg->Version = 0x00000002;
else
DMLCfg->Version = 0x00000001;
DMLCfg->VideoMode |= DML_VID_DML_AUTO;
DMLCfg->Config |= DML_CFG_PADHOOK;
DMLCfg->Config |= DML_CFG_BOOT_DISC;
if(CheatPath != NULL && NewCheatPath != NULL && cheats)
{
char *ptr;
if(strstr(CheatPath, dm_boot_drive) == NULL)
{
fsop_CopyFile(CheatPath, NewCheatPath);
ptr = &NewCheatPath[strlen(dm_boot_drive)];
}
else
ptr = &CheatPath[strlen(dm_boot_drive)];
strncpy(DMLCfg->CheatPath, ptr, sizeof(DMLCfg->CheatPath));
DMLCfg->Config |= DML_CFG_CHEAT_PATH;
}
if(cheats)
{
DMLCfg->Config |= DML_CFG_CHEATS;
printf("cheat path=%s\n",DMLCfg->CheatPath);
printf_x(gt("Ocarina: Searching codes..."));
printf("\n");
sleep(1);
}
if(NMM > 0)
DMLCfg->Config |= DML_CFG_NMM;
if(NMM > 1)
DMLCfg->Config |= DML_CFG_NMM_DEBUG;
if(LED > 0)
DMLCfg->Config |= DML_CFG_ACTIVITY_LED;
if(DMLvideoMode > 3)
DMLCfg->VideoMode |= DML_VID_PROG_PATCH;
//DML v1.2+
memcpy((void *)0x81200000, DMLCfg, sizeof(DML_CFG));
DCFlushRange((void *)(0x81200000), sizeof(DML_CFG));
free(DMLCfg);
}
s32 DML_write_size_info_file(struct discHdr *header, u64 size) {
char filepath[0xFF];
FILE *infoFile = NULL;
snprintf(filepath, sizeof(filepath), "%s/size.bin", header->path);
infoFile = fopen(filepath, "wb");
fwrite(&size, 1, sizeof(u64), infoFile);
fclose(infoFile);
return 0;
}
u64 DML_read_size_info_file(struct discHdr *header) {
char filepath[0xFF];
FILE *infoFile = NULL;
u64 result = 0;
snprintf(filepath, sizeof(filepath), "%s/size.bin", header->path);
infoFile = fopen(filepath, "rb");
if (infoFile) {
fread(&result, 1, sizeof(u64), infoFile);
fclose(infoFile);
}
return result;
}
u64 getDMLDisk1Size(struct discHdr *header) {
u64 result = 0;
char filepath[0xFF];
snprintf(filepath, sizeof(filepath), "%s/game.iso", header->path);
FILE *fp = fopen(filepath, "r");
if (!fp)
{
snprintf(filepath, sizeof(filepath), "%s/sys/boot.bin", header->path);
FILE *fp = fopen(filepath, "r");
if (!fp)
return result;
fclose(fp);
result = DML_read_size_info_file(header);
if (result > 0)
return result;
snprintf(filepath, sizeof(filepath), "%s/root/", header->path);
result = fsop_GetFolderBytes(filepath);
if (result > 0)
DML_write_size_info_file(header, result);
}
else
{
fseek(fp, 0, SEEK_END);
result = ftell(fp);
fclose(fp);
}
return result;
}
u64 getDMLGameSize(struct discHdr *header) {
u64 result = 0;
result = getDMLDisk1Size(header); //get the size of the first disk
char filepath[0xFF];
snprintf(filepath, sizeof(filepath), "%s/disc2.iso", header->path);
FILE *fp = fopen(filepath, "r");
if (fp)
{
fseek(fp, 0, SEEK_END);
result += ftell(fp); //add the size of the second disk
fclose(fp);
}
return result;
}
s32 delete_Old_Copied_DML_Game() {
FILE *infoFile = NULL;
struct discHdr header;
char infoFilePath[255];
sprintf(infoFilePath, "%s/games/lastCopied.bin", dm_boot_drive);
infoFile = fopen(infoFilePath, "rb");
if (infoFile) {
fread(&header, 1, sizeof(struct discHdr), infoFile);
fclose(infoFile);
DML_RemoveGame(header);
return 0;
}
return -1;
}
s32 copy_DML_Game_to_SD(struct discHdr *header) {
char target[255];
if(!strncmp(header->path, dm_boot_drive, strlen(dm_boot_drive))) return -1;
sprintf(target, "%s/games/%s", dm_boot_drive, get_DM_Game_Folder(header->path));
fsop_CopyFolder(header->path, target);
strcpy(header->path, target);
FILE *infoFile = NULL;
char infoFilePath[255];
sprintf(infoFilePath, "%s/games/lastCopied.bin", dm_boot_drive);
infoFile = fopen(infoFilePath, "wb");
fwrite((u8*)header, 1, sizeof(struct discHdr), infoFile);
fclose(infoFile);
return 0;
}
// Devolution
static gconfig *DEVO_CONFIG = (gconfig*)0x80000020;
void DEVO_SetOptions(const char *path, u8 NMM)
{
struct stat st;
char full_path[256];
int data_fd;
stat(path, &st);
u8 *lowmem = (u8*)0x80000000;
FILE *iso_file = fopen(path, "rb");
if(iso_file)
{
printf("path=%s, iso found !!\n",path);
}
fread(lowmem, 1, 32, iso_file);
fclose(iso_file);
// fill out the Devolution config struct
memset(DEVO_CONFIG, 0, sizeof(*DEVO_CONFIG));
DEVO_CONFIG->signature = 0x3EF9DB23;
DEVO_CONFIG->version = 0x00000110;
DEVO_CONFIG->device_signature = st.st_dev;
DEVO_CONFIG->disc1_cluster = st.st_ino;
if (CFG.game.wide_screen)
DEVO_CONFIG->options |= DEVO_CFG_WIDE;
if (!CFG.game.vidtv)
DEVO_CONFIG->options |= DEVO_CFG_NOLED;
if (CFG.game.alt_controller_cfg) {
DEVO_CONFIG->version = 0x00000112;
DEVO_CONFIG->options |= DEVO_CFG_D_BUTTONS;
}
// For 2nd ios file
struct stat st2;
char disc2path[256];
strcpy(disc2path, path);
char *posz = (char *)NULL;
posz = strstr(disc2path, "game.iso");
if(posz != NULL)
strncpy(posz, "disc2.iso", 9);
iso_file = fopen(disc2path, "rb");
if(iso_file)
{
printf("path=%s, iso found !!\n",disc2path);
//sleep(5);
stat(disc2path, &st2);
fread(lowmem, 1, 32, iso_file);
fclose(iso_file);
DEVO_CONFIG->disc2_cluster = st2.st_ino;
}
// make sure these directories exist ON THE DRIVE THAT HOLDS THE ISO FILE, they are required for Devolution to function correctly
char iso_disk[5];
if (path[0] == 'u')
sprintf(iso_disk, "usb:");
else
sprintf(iso_disk, "sd:");
snprintf(full_path, sizeof(full_path), "%s/apps", iso_disk);
fsop_MakeFolder(full_path);
snprintf(full_path, sizeof(full_path), "%s/apps/gc_devo", iso_disk);
fsop_MakeFolder(full_path);
// find or create a 16MB memcard file for emulation
// this file can be located anywhere since it's passed by cluster, not name
// it must be at least 512KB (smallest possible memcard = 59 blocks)
// IT MUST BE LOCATED ON THE SAME DRIVE AS THE ISO FILE!
// IF YOU FUCK THIS UP (I'M LOOKING AT YOU, CFG-LOADER) YOU RISK DATA CORRUPTION
if (USBLOADER_PATH[0] == iso_disk[0])
snprintf(full_path, sizeof(full_path), "%s/memcard.bin", USBLOADER_PATH);
else
snprintf(full_path, sizeof(full_path), "%s/apps/gc_devo/memcard.bin", iso_disk);
// check if file doesn't exist or is less than 16MB
if(stat(full_path, &st) == -1 || st.st_size < 16<<20)
{
// need to enlarge or create it
data_fd = open(full_path, O_WRONLY|O_CREAT);
if (data_fd>=0)
{
// make it 16MB
printf("Resizing memcard file...\n");
ftruncate(data_fd, 16<<20);
if (fstat(data_fd, &st)==-1 || st.st_size < 16<<20)
{
// it still isn't big enough. Give up.
st.st_ino = 0;
}
close(data_fd);
}
else
{
// couldn't open or create the memory card file
st.st_ino = 0;
}
}
// set FAT cluster for start of memory card file
// if this is zero memory card emulation will not be used
if(!NMM > 0)
{
st.st_ino = 0;
printf("emu. memcard is disabled\n");
}
else {
printf("emu. memcard is enabled\n");
DEVO_CONFIG->memcard_cluster = st.st_ino;
}
// flush disc ID and Devolution config out to memory
DCFlushRange(lowmem, 64);
}
char *get_DM_Game_Folder(char *path) {
char *folder;
folder = strstr(path, "/");
folder = folder+1;
while (strstr(folder, "/")) {
folder = strstr(folder, "/");
folder = folder+1;
}
return folder;
}
bool is_gc_game_on_bootable_drive(struct discHdr *header) {
bool ret = false;
if ((CFG.game.channel_boot == 3) || //use nintendont
(CFG.game.channel_boot == 0 && CFG.default_gc_loader == 2))
{
ret = strncmp(header->path, "sd:", 3) == 0 || strncmp(header->path, "usb:", 4) == 0;
}
else
if ((CFG.game.channel_boot == 2) || //use devolution
(CFG.game.channel_boot == 1 && CFG.dml == CFG_MIOS) ||
(CFG.game.channel_boot == 0 && CFG.default_gc_loader == 1))
{
ret = strncmp(header->path, "sd:", 3) == 0 || strncmp(header->path, "usb:", 4) == 0;
}
else //use DIOS MIOS
{
ret = strncmp(header->path, dm_boot_drive, strlen(dm_boot_drive)) == 0;
}
return ret;
}
void Nintendont_set_options(struct discHdr *header, char *CheatPath, char *NewCheatPath)
{
NIN_CFG ncfg;
memset(&ncfg, 0, sizeof(NIN_CFG));
ncfg.Magicbytes = 0x01070CF6;
ncfg.Version = NIN_CFG_VERSION;
if (CFG.game.ocarina)
ncfg.Config |= NIN_CFG_CHEATS;
if (CFG.game.country_patch) //country_patch contains NMM setting
ncfg.Config |= NIN_CFG_MEMCARDEMU;
if (CFG.game.ocarina)
ncfg.Config |= NIN_CFG_CHEAT_PATH;
if (CFG.game.wide_screen)
ncfg.Config |= NIN_CFG_FORCE_WIDE;
if (CFG.game.video == 4 || CFG.game.video == 5)
ncfg.Config |= NIN_CFG_FORCE_PROG;
ncfg.Config |= NIN_CFG_AUTO_BOOT;
if (CFG.game.alt_controller_cfg)
ncfg.Config |= NIN_CFG_HID;
if (strncmp("sd:", header->path, 3))
ncfg.Config |= NIN_CFG_USB;
if (CFG.game.vidtv) //vidtv contains gc led setting
ncfg.Config |= NIN_CFG_LED;
switch (CFG.game.video)
{
default:
case 0:
ncfg.VideoMode |= NIN_VID_AUTO;
break;
case 1:
ncfg.VideoMode |= NIN_VID_FORCE_PAL50 | NIN_VID_FORCE;
break;
case 2:
ncfg.VideoMode |= NIN_VID_FORCE_NTSC | NIN_VID_FORCE;
break;
case 3:
ncfg.VideoMode |= NIN_VID_FORCE_PAL60 | NIN_VID_FORCE;
break;
case 4:
ncfg.VideoMode |= NIN_VID_FORCE_NTSC | NIN_VID_FORCE | NIN_VID_PROG;
break;
case 5:
ncfg.VideoMode |= NIN_VID_FORCE_PAL60 | NIN_VID_FORCE | NIN_VID_PROG;
break;
}
switch (CFG.game.language)
{
case CFG_LANG_CONSOLE:
default:
ncfg.Language = NIN_LAN_AUTO;
break;
case CFG_LANG_ENGLISH:
ncfg.Language = NIN_LAN_ENGLISH;
break;
case CFG_LANG_GERMAN:
ncfg.Language = NIN_LAN_GERMAN;
break;
case CFG_LANG_FRENCH:
ncfg.Language = NIN_LAN_FRENCH;
break;
case CFG_LANG_SPANISH:
ncfg.Language = NIN_LAN_SPANISH;
break;
case CFG_LANG_ITALIAN:
ncfg.Language = NIN_LAN_ITALIAN;
break;
case CFG_LANG_DUTCH:
ncfg.Language = NIN_LAN_DUTCH;
break;
}
if(DML_GameIsInstalled(header->path) == 2) //if FST format
snprintf(ncfg.GamePath, sizeof(ncfg.GamePath), "%s/", strstr(header->path, "/"));
else
snprintf(ncfg.GamePath, sizeof(ncfg.GamePath), "%s/game.iso", strstr(header->path, "/"));
if(CFG.game.ocarina && strstr(CheatPath, NewCheatPath) == NULL)
fsop_CopyFile(CheatPath, NewCheatPath);
snprintf(ncfg.CheatPath, sizeof(ncfg.CheatPath), "%s", strstr(NewCheatPath, "/"));
ncfg.MaxPads = NIN_CFG_MAXPAD;
memcpy(&ncfg.GameID, header->id, 4);
FILE *f;
f = fopen("/nincfg.bin", "wb");
fwrite(&ncfg, 1, sizeof(NIN_CFG), f);
fclose(f);
}