mirror of
https://github.com/melonDS-emu/melonDS.git
synced 2025-06-28 09:59:41 -06:00
get some of the shit going, I guess
atleast the emuthread is going and we have its control system down and other fun shit, too
This commit is contained in:
@ -22,20 +22,364 @@
|
||||
#include <string.h>
|
||||
|
||||
#include <QApplication>
|
||||
#include <QMainWindow>
|
||||
#include <QMessageBox>
|
||||
#include <QMenuBar>
|
||||
#include <QFileDialog>
|
||||
#include <QPaintEvent>
|
||||
#include <QPainter>
|
||||
|
||||
#include <SDL2/SDL.h>
|
||||
|
||||
#include "main.h"
|
||||
|
||||
#include "../../version.h"
|
||||
#include "types.h"
|
||||
#include "version.h"
|
||||
|
||||
#include "NDS.h"
|
||||
#include "GBACart.h"
|
||||
#include "GPU.h"
|
||||
#include "SPU.h"
|
||||
#include "Wifi.h"
|
||||
#include "Platform.h"
|
||||
#include "Config.h"
|
||||
|
||||
#include "Savestate.h"
|
||||
|
||||
|
||||
char* EmuDirectory;
|
||||
|
||||
bool RunningSomething;
|
||||
char ROMPath[2][1024];
|
||||
char SRAMPath[2][1024];
|
||||
char PrevSRAMPath[2][1024]; // for savestate 'undo load'
|
||||
|
||||
bool SavestateLoaded;
|
||||
|
||||
MainWindow* mainWindow;
|
||||
EmuThread* emuThread;
|
||||
|
||||
|
||||
EmuThread::EmuThread(QObject* parent) : QThread(parent)
|
||||
{
|
||||
EmuStatus = 0;
|
||||
EmuRunning = 2;
|
||||
}
|
||||
|
||||
void EmuThread::run()
|
||||
{
|
||||
NDS::Init();
|
||||
|
||||
/*MainScreenPos[0] = 0;
|
||||
MainScreenPos[1] = 0;
|
||||
MainScreenPos[2] = 0;
|
||||
AutoScreenSizing = 0;*/
|
||||
|
||||
/*if (Screen_UseGL)
|
||||
{
|
||||
uiGLMakeContextCurrent(GLContext);
|
||||
GPU3D::InitRenderer(true);
|
||||
uiGLMakeContextCurrent(NULL);
|
||||
}
|
||||
else*/
|
||||
{
|
||||
GPU3D::InitRenderer(false);
|
||||
}
|
||||
|
||||
/*Touching = false;
|
||||
KeyInputMask = 0xFFF;
|
||||
JoyInputMask = 0xFFF;
|
||||
KeyHotkeyMask = 0;
|
||||
JoyHotkeyMask = 0;
|
||||
HotkeyMask = 0;
|
||||
LastHotkeyMask = 0;
|
||||
LidStatus = false;*/
|
||||
|
||||
u32 nframes = 0;
|
||||
u32 starttick = SDL_GetTicks();
|
||||
u32 lasttick = starttick;
|
||||
u32 lastmeasuretick = lasttick;
|
||||
u32 fpslimitcount = 0;
|
||||
u64 perfcount = SDL_GetPerformanceCounter();
|
||||
u64 perffreq = SDL_GetPerformanceFrequency();
|
||||
float samplesleft = 0;
|
||||
u32 nsamples = 0;
|
||||
|
||||
char melontitle[100];
|
||||
SDL_mutex* titlemutex = SDL_CreateMutex();
|
||||
void* titledata[2] = {melontitle, titlemutex};
|
||||
printf("emu thread start: %d\n", EmuRunning);
|
||||
while (EmuRunning != 0)
|
||||
{
|
||||
/*ProcessInput();
|
||||
|
||||
if (HotkeyPressed(HK_FastForwardToggle))
|
||||
{
|
||||
Config::LimitFPS = !Config::LimitFPS;
|
||||
uiQueueMain(UpdateFPSLimit, NULL);
|
||||
}
|
||||
// TODO: similar hotkeys for video/audio sync?
|
||||
|
||||
if (HotkeyPressed(HK_Pause)) uiQueueMain(TogglePause, NULL);
|
||||
if (HotkeyPressed(HK_Reset)) uiQueueMain(Reset, NULL);
|
||||
|
||||
if (GBACart::CartInserted && GBACart::HasSolarSensor)
|
||||
{
|
||||
if (HotkeyPressed(HK_SolarSensorDecrease))
|
||||
{
|
||||
if (GBACart_SolarSensor::LightLevel > 0) GBACart_SolarSensor::LightLevel--;
|
||||
char msg[64];
|
||||
sprintf(msg, "Solar sensor level set to %d", GBACart_SolarSensor::LightLevel);
|
||||
OSD::AddMessage(0, msg);
|
||||
}
|
||||
if (HotkeyPressed(HK_SolarSensorIncrease))
|
||||
{
|
||||
if (GBACart_SolarSensor::LightLevel < 10) GBACart_SolarSensor::LightLevel++;
|
||||
char msg[64];
|
||||
sprintf(msg, "Solar sensor level set to %d", GBACart_SolarSensor::LightLevel);
|
||||
OSD::AddMessage(0, msg);
|
||||
}
|
||||
}*/
|
||||
|
||||
if (EmuRunning == 1)
|
||||
{
|
||||
EmuStatus = 1;
|
||||
|
||||
// process input and hotkeys
|
||||
NDS::SetKeyMask(0xFFF);
|
||||
/*NDS::SetKeyMask(KeyInputMask & JoyInputMask);
|
||||
|
||||
if (HotkeyPressed(HK_Lid))
|
||||
{
|
||||
LidStatus = !LidStatus;
|
||||
NDS::SetLidClosed(LidStatus);
|
||||
OSD::AddMessage(0, LidStatus ? "Lid closed" : "Lid opened");
|
||||
}*/
|
||||
|
||||
// microphone input
|
||||
/*FeedMicInput();
|
||||
|
||||
if (Screen_UseGL)
|
||||
{
|
||||
uiGLBegin(GLContext);
|
||||
uiGLMakeContextCurrent(GLContext);
|
||||
}*/
|
||||
|
||||
// auto screen layout
|
||||
/*{
|
||||
MainScreenPos[2] = MainScreenPos[1];
|
||||
MainScreenPos[1] = MainScreenPos[0];
|
||||
MainScreenPos[0] = NDS::PowerControl9 >> 15;
|
||||
|
||||
int guess;
|
||||
if (MainScreenPos[0] == MainScreenPos[2] &&
|
||||
MainScreenPos[0] != MainScreenPos[1])
|
||||
{
|
||||
// constant flickering, likely displaying 3D on both screens
|
||||
// TODO: when both screens are used for 2D only...???
|
||||
guess = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (MainScreenPos[0] == 1)
|
||||
guess = 1;
|
||||
else
|
||||
guess = 2;
|
||||
}
|
||||
|
||||
if (guess != AutoScreenSizing)
|
||||
{
|
||||
AutoScreenSizing = guess;
|
||||
SetupScreenRects(WindowWidth, WindowHeight);
|
||||
}
|
||||
}*/
|
||||
|
||||
// emulate
|
||||
u32 nlines = NDS::RunFrame();
|
||||
|
||||
#ifdef MELONCAP
|
||||
MelonCap::Update();
|
||||
#endif // MELONCAP
|
||||
|
||||
if (EmuRunning == 0) break;
|
||||
|
||||
/*if (Screen_UseGL)
|
||||
{
|
||||
GLScreen_DrawScreen();
|
||||
uiGLEnd(GLContext);
|
||||
}
|
||||
uiAreaQueueRedrawAll(MainDrawArea);*/
|
||||
|
||||
/*bool fastforward = HotkeyDown(HK_FastForward);
|
||||
|
||||
if (Config::AudioSync && !fastforward)
|
||||
{
|
||||
SDL_LockMutex(AudioSyncLock);
|
||||
while (SPU::GetOutputSize() > 1024)
|
||||
{
|
||||
int ret = SDL_CondWaitTimeout(AudioSync, AudioSyncLock, 500);
|
||||
if (ret == SDL_MUTEX_TIMEDOUT) break;
|
||||
}
|
||||
SDL_UnlockMutex(AudioSyncLock);
|
||||
}
|
||||
|
||||
float framerate = (1000.0f * nlines) / (60.0f * 263.0f);
|
||||
|
||||
{
|
||||
u32 curtick = SDL_GetTicks();
|
||||
u32 delay = curtick - lasttick;
|
||||
|
||||
bool limitfps = Config::LimitFPS && !fastforward;
|
||||
if (limitfps)
|
||||
{
|
||||
float wantedtickF = starttick + (framerate * (fpslimitcount+1));
|
||||
u32 wantedtick = (u32)ceil(wantedtickF);
|
||||
if (curtick < wantedtick) SDL_Delay(wantedtick - curtick);
|
||||
|
||||
lasttick = SDL_GetTicks();
|
||||
fpslimitcount++;
|
||||
if ((abs(wantedtickF - (float)wantedtick) < 0.001312) || (fpslimitcount > 60))
|
||||
{
|
||||
fpslimitcount = 0;
|
||||
nsamples = 0;
|
||||
starttick = lasttick;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (delay < 1) SDL_Delay(1);
|
||||
lasttick = SDL_GetTicks();
|
||||
}
|
||||
}
|
||||
|
||||
nframes++;
|
||||
if (nframes >= 30)
|
||||
{
|
||||
u32 tick = SDL_GetTicks();
|
||||
u32 diff = tick - lastmeasuretick;
|
||||
lastmeasuretick = tick;
|
||||
|
||||
u32 fps;
|
||||
if (diff < 1) fps = 77777;
|
||||
else fps = (nframes * 1000) / diff;
|
||||
nframes = 0;
|
||||
|
||||
float fpstarget;
|
||||
if (framerate < 1) fpstarget = 999;
|
||||
else fpstarget = 1000.0f/framerate;
|
||||
|
||||
SDL_LockMutex(titlemutex);
|
||||
sprintf(melontitle, "[%d/%.0f] melonDS " MELONDS_VERSION, fps, fpstarget);
|
||||
SDL_UnlockMutex(titlemutex);
|
||||
uiQueueMain(UpdateWindowTitle, titledata);
|
||||
}*/
|
||||
}
|
||||
else
|
||||
{
|
||||
// paused
|
||||
nframes = 0;
|
||||
lasttick = SDL_GetTicks();
|
||||
starttick = lasttick;
|
||||
lastmeasuretick = lasttick;
|
||||
fpslimitcount = 0;
|
||||
|
||||
if (EmuRunning == 2)
|
||||
{
|
||||
/*if (Screen_UseGL)
|
||||
{
|
||||
uiGLBegin(GLContext);
|
||||
uiGLMakeContextCurrent(GLContext);
|
||||
GLScreen_DrawScreen();
|
||||
uiGLEnd(GLContext);
|
||||
}
|
||||
uiAreaQueueRedrawAll(MainDrawArea);*/
|
||||
}
|
||||
|
||||
//if (Screen_UseGL) uiGLMakeContextCurrent(NULL);
|
||||
|
||||
EmuStatus = EmuRunning;
|
||||
|
||||
SDL_Delay(100);
|
||||
}
|
||||
printf("ran iteration: status=%d run=%d\n", EmuStatus, EmuRunning);
|
||||
}
|
||||
|
||||
EmuStatus = 0;
|
||||
|
||||
SDL_DestroyMutex(titlemutex);
|
||||
|
||||
//if (Screen_UseGL) uiGLMakeContextCurrent(GLContext);
|
||||
|
||||
NDS::DeInit();
|
||||
//Platform::LAN_DeInit();
|
||||
|
||||
/*if (Screen_UseGL)
|
||||
{
|
||||
OSD::DeInit(true);
|
||||
GLScreen_DeInit();
|
||||
}
|
||||
else
|
||||
OSD::DeInit(false);*/
|
||||
|
||||
//if (Screen_UseGL) uiGLMakeContextCurrent(NULL);
|
||||
}
|
||||
|
||||
void EmuThread::emuRun()
|
||||
{
|
||||
EmuRunning = 1;
|
||||
}
|
||||
|
||||
void EmuThread::emuPause(bool refresh)
|
||||
{
|
||||
int status = refresh ? 2:3;
|
||||
PrevEmuStatus = EmuRunning;
|
||||
EmuRunning = status;printf("emuPause %d -> %d %d\n", PrevEmuStatus, EmuRunning, EmuStatus);
|
||||
while (EmuStatus != status);printf("wait done\n");
|
||||
}
|
||||
|
||||
void EmuThread::emuUnpause()
|
||||
{
|
||||
EmuRunning = PrevEmuStatus;
|
||||
}
|
||||
|
||||
void EmuThread::emuStop()
|
||||
{
|
||||
EmuRunning = 0;
|
||||
}
|
||||
|
||||
|
||||
MainWindowPanel::MainWindowPanel(QWidget* parent) : QWidget(parent)
|
||||
{
|
||||
}
|
||||
|
||||
MainWindowPanel::~MainWindowPanel()
|
||||
{
|
||||
}
|
||||
|
||||
void MainWindowPanel::paintEvent(QPaintEvent* event)
|
||||
{
|
||||
QPainter painter(this);
|
||||
|
||||
//painter.
|
||||
}
|
||||
|
||||
|
||||
MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent)
|
||||
{
|
||||
setWindowTitle("melonDS - assfucking Qt version");
|
||||
|
||||
// burp
|
||||
QWidget *centralWidget = new QWidget(this);
|
||||
setCentralWidget(centralWidget);
|
||||
QMenuBar* menubar = new QMenuBar();
|
||||
{
|
||||
QMenu* menu = menubar->addMenu("File");
|
||||
QAction* act;
|
||||
|
||||
act = menu->addAction("Open file...");
|
||||
connect(act, &QAction::triggered, this, &MainWindow::onOpenFile);
|
||||
}
|
||||
setMenuBar(menubar);
|
||||
|
||||
panel = new MainWindowPanel(this);
|
||||
setCentralWidget(panel);
|
||||
panel->setMinimumSize(256, 384);
|
||||
}
|
||||
|
||||
MainWindow::~MainWindow()
|
||||
@ -43,6 +387,13 @@ MainWindow::~MainWindow()
|
||||
}
|
||||
|
||||
|
||||
void MainWindow::onOpenFile()
|
||||
{
|
||||
QString filename = QFileDialog::getOpenFileName(this, "Open ROM", "", "DS ROMs (*.nds *.srl);;Any file (*.*)");
|
||||
printf("fark: %p %d %s\n", filename, filename.isEmpty(), filename.toStdString().c_str());
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
srand(time(NULL));
|
||||
@ -50,12 +401,212 @@ int main(int argc, char** argv)
|
||||
printf("melonDS " MELONDS_VERSION "\n");
|
||||
printf(MELONDS_URL "\n");
|
||||
|
||||
#if defined(__WIN32__) || defined(UNIX_PORTABLE)
|
||||
if (argc > 0 && strlen(argv[0]) > 0)
|
||||
{
|
||||
int len = strlen(argv[0]);
|
||||
while (len > 0)
|
||||
{
|
||||
if (argv[0][len] == '/') break;
|
||||
if (argv[0][len] == '\\') break;
|
||||
len--;
|
||||
}
|
||||
if (len > 0)
|
||||
{
|
||||
EmuDirectory = new char[len+1];
|
||||
strncpy(EmuDirectory, argv[0], len);
|
||||
EmuDirectory[len] = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
EmuDirectory = new char[2];
|
||||
strcpy(EmuDirectory, ".");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EmuDirectory = new char[2];
|
||||
strcpy(EmuDirectory, ".");
|
||||
}
|
||||
#else
|
||||
const char* confdir = g_get_user_config_dir();
|
||||
const char* confname = "/melonDS";
|
||||
EmuDirectory = new char[strlen(confdir) + strlen(confname) + 1];
|
||||
strcat(EmuDirectory, confdir);
|
||||
strcat(EmuDirectory, confname);
|
||||
#endif
|
||||
|
||||
QApplication melon(argc, argv);
|
||||
|
||||
MainWindow win;
|
||||
win.show();
|
||||
// http://stackoverflow.com/questions/14543333/joystick-wont-work-using-sdl
|
||||
SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1");
|
||||
|
||||
return melon.exec();
|
||||
if (SDL_Init(SDL_INIT_HAPTIC) < 0)
|
||||
{
|
||||
printf("SDL couldn't init rumble\n");
|
||||
}
|
||||
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK) < 0)
|
||||
{
|
||||
QMessageBox::critical(NULL, "melonDS", "SDL shat itself :(");
|
||||
return 1;
|
||||
}
|
||||
|
||||
SDL_JoystickEventState(SDL_ENABLE);
|
||||
|
||||
Config::Load();
|
||||
|
||||
//if (Config::AudioVolume < 0) Config::AudioVolume = 0;
|
||||
//else if (Config::AudioVolume > 256) Config::AudioVolume = 256;
|
||||
|
||||
// TODO: those should be checked before running anything
|
||||
// (as to let the user specify their own BIOS/firmware path etc)
|
||||
#if 0
|
||||
if (!Platform::LocalFileExists("bios7.bin") ||
|
||||
!Platform::LocalFileExists("bios9.bin") ||
|
||||
!Platform::LocalFileExists("firmware.bin"))
|
||||
{
|
||||
#if defined(__WIN32__) || defined(UNIX_PORTABLE)
|
||||
const char* locationName = "the directory you run melonDS from";
|
||||
#else
|
||||
char* locationName = EmuDirectory;
|
||||
#endif
|
||||
char msgboxtext[512];
|
||||
sprintf(msgboxtext,
|
||||
"One or more of the following required files don't exist or couldn't be accessed:\n\n"
|
||||
"bios7.bin -- ARM7 BIOS\n"
|
||||
"bios9.bin -- ARM9 BIOS\n"
|
||||
"firmware.bin -- firmware image\n\n"
|
||||
"Dump the files from your DS and place them in %s.\n"
|
||||
"Make sure that the files can be accessed.",
|
||||
locationName
|
||||
);
|
||||
|
||||
uiMsgBoxError(NULL, "BIOS/Firmware not found", msgboxtext);
|
||||
|
||||
uiUninit();
|
||||
SDL_Quit();
|
||||
return 0;
|
||||
}
|
||||
if (!Platform::LocalFileExists("firmware.bin.bak"))
|
||||
{
|
||||
// verify the firmware
|
||||
//
|
||||
// there are dumps of an old hacked firmware floating around on the internet
|
||||
// and those are problematic
|
||||
// the hack predates WFC, and, due to this, any game that alters the WFC
|
||||
// access point data will brick that firmware due to it having critical
|
||||
// data in the same area. it has the same problem on hardware.
|
||||
//
|
||||
// but this should help stop users from reporting that issue over and over
|
||||
// again, when the issue is not from melonDS but from their firmware dump.
|
||||
//
|
||||
// I don't know about all the firmware hacks in existence, but the one I
|
||||
// looked at has 0x180 bytes from the header repeated at 0x3FC80, but
|
||||
// bytes 0x0C-0x14 are different.
|
||||
|
||||
FILE* f = Platform::OpenLocalFile("firmware.bin", "rb");
|
||||
u8 chk1[0x180], chk2[0x180];
|
||||
|
||||
fseek(f, 0, SEEK_SET);
|
||||
fread(chk1, 1, 0x180, f);
|
||||
fseek(f, -0x380, SEEK_END);
|
||||
fread(chk2, 1, 0x180, f);
|
||||
|
||||
memset(&chk1[0x0C], 0, 8);
|
||||
memset(&chk2[0x0C], 0, 8);
|
||||
|
||||
fclose(f);
|
||||
|
||||
if (!memcmp(chk1, chk2, 0x180))
|
||||
{
|
||||
uiMsgBoxError(NULL,
|
||||
"Problematic firmware dump",
|
||||
"You are using an old hacked firmware dump.\n"
|
||||
"Firmware boot will stop working if you run any game that alters WFC settings.\n\n"
|
||||
"Note that the issue is not from melonDS, it would also happen on an actual DS.");
|
||||
}
|
||||
}
|
||||
{
|
||||
const char* romlist_missing = "Save memory type detection will not work correctly.\n\n"
|
||||
"You should use the latest version of romlist.bin (provided in melonDS release packages).";
|
||||
#if !defined(UNIX_PORTABLE) && !defined(__WIN32__)
|
||||
std::string missingstr = std::string(romlist_missing) +
|
||||
"\n\nThe ROM list should be placed in " + g_get_user_data_dir() + "/melonds/, otherwise "
|
||||
"melonDS will search for it in the current working directory.";
|
||||
const char* romlist_missing_text = missingstr.c_str();
|
||||
#else
|
||||
const char* romlist_missing_text = romlist_missing;
|
||||
#endif
|
||||
|
||||
FILE* f = Platform::OpenDataFile("romlist.bin");
|
||||
if (f)
|
||||
{
|
||||
u32 data;
|
||||
fread(&data, 4, 1, f);
|
||||
fclose(f);
|
||||
|
||||
if ((data >> 24) == 0) // old CRC-based list
|
||||
{
|
||||
uiMsgBoxError(NULL, "Your version of romlist.bin is outdated.", romlist_missing_text);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
uiMsgBoxError(NULL, "romlist.bin not found.", romlist_missing_text);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
mainWindow = new MainWindow();
|
||||
mainWindow->show();
|
||||
|
||||
emuThread = new EmuThread();
|
||||
emuThread->start();
|
||||
emuThread->emuPause(true);
|
||||
|
||||
if (argc > 1)
|
||||
{
|
||||
char* file = argv[1];
|
||||
char* ext = &file[strlen(file)-3];
|
||||
|
||||
if (!strcasecmp(ext, "nds") || !strcasecmp(ext, "srl"))
|
||||
{
|
||||
strncpy(ROMPath[0], file, 1023);
|
||||
ROMPath[0][1023] = '\0';
|
||||
|
||||
//SetupSRAMPath(0);
|
||||
|
||||
//if (NDS::LoadROM(ROMPath[0], SRAMPath[0], Config::DirectBoot))
|
||||
// Run();
|
||||
}
|
||||
|
||||
if (argc > 2)
|
||||
{
|
||||
file = argv[2];
|
||||
ext = &file[strlen(file)-3];
|
||||
|
||||
if (!strcasecmp(ext, "gba"))
|
||||
{
|
||||
strncpy(ROMPath[1], file, 1023);
|
||||
ROMPath[1][1023] = '\0';
|
||||
|
||||
//SetupSRAMPath(1);
|
||||
|
||||
//NDS::LoadGBAROM(ROMPath[1], SRAMPath[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int ret = melon.exec();
|
||||
printf("melon over\n");
|
||||
emuThread->emuStop();printf("STOP\n");
|
||||
emuThread->wait();printf("farked\n");
|
||||
|
||||
Config::Save();
|
||||
|
||||
SDL_Quit();
|
||||
delete[] EmuDirectory;
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef __WIN32__
|
||||
@ -71,6 +622,7 @@ int CALLBACK WinMain(HINSTANCE hinst, HINSTANCE hprev, LPSTR cmdline, int cmdsho
|
||||
char** argv = new char*[argc];
|
||||
for (int i = 0; i < argc; i++)
|
||||
{
|
||||
if (!argv_w) { argv[i] = nullarg; continue; }
|
||||
int len = WideCharToMultiByte(CP_UTF8, 0, argv_w[i], -1, NULL, 0, NULL, NULL);
|
||||
if (len < 1) { argv[i] = nullarg; continue; }
|
||||
argv[i] = new char[len];
|
||||
@ -78,6 +630,8 @@ int CALLBACK WinMain(HINSTANCE hinst, HINSTANCE hprev, LPSTR cmdline, int cmdsho
|
||||
if (res != len) { delete[] argv[i]; argv[i] = nullarg; }
|
||||
}
|
||||
|
||||
if (argv_w) LocalFree(argv_w);
|
||||
|
||||
if (AttachConsole(ATTACH_PARENT_PROCESS))
|
||||
{
|
||||
freopen("CONOUT$", "w", stdout);
|
||||
|
Reference in New Issue
Block a user