mirror of
https://github.com/reactos/reactos.git
synced 2024-08-19 18:02:55 +00:00
![Pierre Schweitzer](/assets/img/avatar_default.png)
floppy drives in ReactOS and mount images on them. Only the cmd got imported. The GUI interface may come later on. Note that, as for vcdrom, the driver is left disabled and you need to explicitely start it through vfd command line interface. CORE-14090
3499 lines
64 KiB
C
3499 lines
64 KiB
C
/*
|
|
vfdcmd.c
|
|
|
|
Virtual Floppy Drive for Windows
|
|
Driver control program (console version)
|
|
|
|
Copyright (C) 2003-2008 Ken Kato
|
|
*/
|
|
|
|
#ifdef __cplusplus
|
|
#pragma message(__FILE__": Compiled as C++ for testing purpose.")
|
|
#endif // __cplusplus
|
|
|
|
#define WIN32_LEAN_AND_MEAN
|
|
#define _CRTDBG_MAP_ALLOC
|
|
#include <windows.h>
|
|
#include <stdio.h>
|
|
#include <stdarg.h>
|
|
#include <stdlib.h>
|
|
#include <crtdbg.h>
|
|
|
|
#ifndef INVALID_FILE_ATTRIBUTES
|
|
#define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
|
|
#endif // INVALID_FILE_ATTRIBUTES
|
|
|
|
#include "vfdtypes.h"
|
|
#include "vfdapi.h"
|
|
#include "vfdver.h"
|
|
#include "vfdmsg.h"
|
|
|
|
//
|
|
// current driver state
|
|
//
|
|
static DWORD driver_state = VFD_NOT_INSTALLED;
|
|
|
|
//
|
|
// interactive flag
|
|
//
|
|
static const char *help_progname = "VFD.EXE ";
|
|
|
|
//
|
|
// command functions return value
|
|
//
|
|
#define VFD_OK 0
|
|
#define VFD_NG 1
|
|
|
|
//
|
|
// operation mode
|
|
//
|
|
#define OPERATION_ASK 0 // ask user on error
|
|
#define OPERATION_QUIT 1 // quits on error
|
|
#define OPERATION_FORCE 2 // force on error
|
|
|
|
//
|
|
// invalid target number
|
|
//
|
|
#define TARGET_NONE (ULONG)-1
|
|
|
|
//
|
|
// command processing functions
|
|
//
|
|
typedef int (*cmdfnc)(const char **args);
|
|
|
|
static int Install(const char **args);
|
|
static int Remove(const char **args);
|
|
static int Config(const char **args);
|
|
static int Start(const char **args);
|
|
static int Stop(const char **args);
|
|
static int Shell(const char **args);
|
|
static int Open(const char **args);
|
|
static int Close(const char **args);
|
|
static int Save(const char **args);
|
|
static int Protect(const char **args);
|
|
static int Format(const char **args);
|
|
static int Link(const char **args);
|
|
static int Unlink(const char **args);
|
|
static int Status(const char **args);
|
|
static int Help(const char **args);
|
|
static int Version(const char **args);
|
|
|
|
//
|
|
// Command table
|
|
//
|
|
static const struct {
|
|
char *cmd; // command string
|
|
int max_args; // maximum allowed number of argc
|
|
cmdfnc func; // command processing function
|
|
DWORD hint; // command hint message id
|
|
}
|
|
Commands[] = {
|
|
{"INSTALL", 2, Install, MSG_HINT_INSTALL},
|
|
{"REMOVE", 1, Remove, MSG_HINT_REMOVE },
|
|
{"CONFIG", 1, Config, MSG_HINT_CONFIG },
|
|
{"START", 0, Start, MSG_HINT_START },
|
|
{"STOP", 1, Stop, MSG_HINT_STOP },
|
|
{"SHELL", 1, Shell, MSG_HINT_SHELL },
|
|
{"OPEN", 6, Open, MSG_HINT_OPEN },
|
|
{"CLOSE", 2, Close, MSG_HINT_CLOSE },
|
|
{"SAVE", 3, Save, MSG_HINT_SAVE, },
|
|
{"PROTECT", 2, Protect, MSG_HINT_PROTECT},
|
|
{"FORMAT", 2, Format, MSG_HINT_FORMAT },
|
|
{"LINK", 3, Link, MSG_HINT_LINK },
|
|
{"ULINK", 1, Unlink, MSG_HINT_ULINK },
|
|
{"STATUS", 0, Status, MSG_HINT_STATUS },
|
|
{"HELP", 1, Help, MSG_HELP_HELP },
|
|
{"?", 1, Help, MSG_HELP_HELP },
|
|
{"VERSION", 0, Version, MSG_HINT_VERSION},
|
|
{0, 0, 0, 0}
|
|
};
|
|
|
|
//
|
|
// Help message table
|
|
//
|
|
static const struct {
|
|
char *keyword; // help keyword
|
|
DWORD help; // help message id
|
|
}
|
|
HelpMsg[] = {
|
|
{"GENERAL", MSG_HELP_GENERAL},
|
|
{"CONSOLE", MSG_HELP_CONSOLE},
|
|
{"INSTALL", MSG_HELP_INSTALL},
|
|
{"REMOVE", MSG_HELP_REMOVE },
|
|
{"CONFIG", MSG_HELP_CONFIG },
|
|
{"START", MSG_HELP_START },
|
|
{"STOP", MSG_HELP_STOP },
|
|
{"SHELL", MSG_HELP_SHELL },
|
|
{"OPEN", MSG_HELP_OPEN },
|
|
{"CLOSE", MSG_HELP_CLOSE },
|
|
{"SAVE", MSG_HELP_SAVE },
|
|
{"PROTECT", MSG_HELP_PROTECT},
|
|
{"FORMAT", MSG_HELP_FORMAT },
|
|
{"LINK", MSG_HELP_LINK },
|
|
{"ULINK", MSG_HELP_ULINK },
|
|
{"STATUS", MSG_HELP_STATUS },
|
|
{"HELP", MSG_HELP_HELP },
|
|
{"VERSION", MSG_HINT_VERSION},
|
|
{0, 0}
|
|
};
|
|
|
|
//
|
|
// local functions
|
|
//
|
|
static int InteractiveConsole();
|
|
static int ProcessCommandLine(int argc, const char **args);
|
|
static int ParseCommand(const char *cmd);
|
|
static int ParseHelpTopic(const char *topic);
|
|
static int CheckDriver();
|
|
static int InputChar(ULONG msg, PCSTR ans);
|
|
static void PrintImageInfo(HANDLE hDevice);
|
|
static void PrintDriveLetter(HANDLE hDevice, ULONG nDrive);
|
|
static void PrintMessage(UINT msg, ...);
|
|
static BOOL ConsolePager(char *pBuffer, BOOL bReset);
|
|
static const char *SystemError(DWORD err);
|
|
static void ConvertPathCase(char *src, char *dst);
|
|
|
|
//
|
|
// utility macro
|
|
//
|
|
#define IS_WINDOWS_NT() ((GetVersion() & 0xff) < 5)
|
|
|
|
//
|
|
// main
|
|
//
|
|
int main(int argc, const char **argv)
|
|
{
|
|
#ifdef _DEBUG
|
|
|
|
// output vfd.exe command reference text
|
|
|
|
if (*(argv + 1) && !_stricmp(*(argv + 1), "doc")) {
|
|
int idx = 0;
|
|
char *buf = "";
|
|
|
|
printf("\r\n VFD.EXE Command Reference\r\n");
|
|
|
|
while (HelpMsg[idx].keyword) {
|
|
int len = strlen(HelpMsg[idx].keyword);
|
|
|
|
printf(
|
|
"\r\n\r\n"
|
|
"====================\r\n"
|
|
"%*s\r\n"
|
|
"====================\r\n"
|
|
"\r\n",
|
|
(20 + len) / 2, HelpMsg[idx].keyword);
|
|
|
|
FormatMessage(
|
|
FORMAT_MESSAGE_FROM_HMODULE |
|
|
FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
|
FORMAT_MESSAGE_ARGUMENT_ARRAY,
|
|
NULL, HelpMsg[idx].help, 0,
|
|
(LPTSTR)&buf, 0, (va_list *)&help_progname);
|
|
|
|
printf("%s", buf);
|
|
|
|
LocalFree(buf);
|
|
|
|
idx++;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
// Reports memory leaks at process termination
|
|
|
|
_CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _CRTDBG_LEAK_CHECK_DF);
|
|
|
|
// Check the operating system version
|
|
|
|
if (!VfdIsValidPlatform()) {
|
|
PrintMessage(MSG_WRONG_PLATFORM);
|
|
return VFD_NG;
|
|
}
|
|
|
|
if (argc < 2) {
|
|
// If no parameter is given, enter the interactive mode
|
|
|
|
return InteractiveConsole();
|
|
}
|
|
else {
|
|
// Perform a single operation
|
|
|
|
return ProcessCommandLine(argc - 1, argv + 1);
|
|
}
|
|
}
|
|
|
|
//
|
|
// VFD interactive console
|
|
//
|
|
int InteractiveConsole()
|
|
{
|
|
char input[1024]; // user input buffer
|
|
|
|
int argc; // number of args in the user input
|
|
char *args[10]; // args to pass to command functions
|
|
|
|
char sepa; // argument separator
|
|
char *p; // work pointer
|
|
|
|
// Disable the system default Ctrl+C handler
|
|
|
|
SetConsoleCtrlHandler(NULL, TRUE);
|
|
|
|
// Set the console title
|
|
|
|
SetConsoleTitle(VFD_PRODUCT_DESC);
|
|
|
|
// print version information and the console hint text
|
|
|
|
Version(NULL);
|
|
|
|
PrintMessage(MSG_CONSOLE_HINT);
|
|
|
|
// set interactive flag to exclude "VFD.EXE" from help text
|
|
|
|
help_progname = "";
|
|
|
|
// process user input
|
|
|
|
for (;;) {
|
|
|
|
// print the prompt
|
|
|
|
printf("[VFD] ");
|
|
fflush(stdout);
|
|
|
|
// read user input
|
|
|
|
fflush(stdin);
|
|
p = fgets(input, sizeof(input), stdin);
|
|
|
|
if (p == NULL) {
|
|
|
|
// most likely <ctrl+c>
|
|
|
|
printf("exit\n");
|
|
break;
|
|
}
|
|
|
|
// skip leading blank characters
|
|
|
|
while (*p == ' ' || *p == '\t' || *p == '\n') {
|
|
p++;
|
|
}
|
|
|
|
if (*p == '\0') {
|
|
|
|
// empty input
|
|
|
|
continue;
|
|
}
|
|
|
|
// handle external commands
|
|
|
|
if (!_strnicmp(p, "dir", 3) ||
|
|
!_strnicmp(p, "attrib", 6)) {
|
|
|
|
// special cases - frequently used commands
|
|
// pass these to system() even without '.'
|
|
|
|
system(p);
|
|
printf("\n");
|
|
continue;
|
|
}
|
|
else if (*p == '.') {
|
|
|
|
// external command
|
|
|
|
system(p + 1);
|
|
printf("\n");
|
|
continue;
|
|
}
|
|
|
|
// split the input line into parameters (10 parameters max)
|
|
|
|
argc = 0;
|
|
ZeroMemory(args, sizeof(args));
|
|
|
|
do {
|
|
// top of a parameter
|
|
|
|
args[argc++] = p;
|
|
|
|
// is the parameter quoted?
|
|
|
|
if (*p == '\"' || *p == '\'') {
|
|
sepa = *(p++);
|
|
}
|
|
else {
|
|
sepa = ' ';
|
|
}
|
|
|
|
// search the end of the parameter
|
|
|
|
while (*p && *p != '\n') {
|
|
if (sepa == ' ') {
|
|
if (*p == '\t' || *p == ' ') {
|
|
break; // tail of a non-quoted parameter
|
|
}
|
|
}
|
|
else {
|
|
if (*p == sepa) {
|
|
sepa = ' '; // close quote
|
|
}
|
|
}
|
|
p++;
|
|
}
|
|
|
|
// terminate the parameter
|
|
|
|
if (*p) {
|
|
*(p++) = '\0';
|
|
}
|
|
|
|
// skip trailing blank characters
|
|
|
|
while (*p == ' ' || *p == '\t' || *p == '\n') {
|
|
p++;
|
|
}
|
|
|
|
if (*p == '\0') {
|
|
|
|
// end of the input line - no more args
|
|
|
|
break;
|
|
}
|
|
}
|
|
while (argc < sizeof(args) / sizeof(args[0]));
|
|
|
|
// check the first parameter for special commands
|
|
|
|
if (!_stricmp(args[0], "exit") ||
|
|
!_stricmp(args[0], "quit") ||
|
|
!_stricmp(args[0], "bye")) {
|
|
|
|
// exit command
|
|
|
|
break;
|
|
}
|
|
else if (!_stricmp(args[0], "cd") ||
|
|
!_stricmp(args[0], "chdir")) {
|
|
|
|
// internal change directory command
|
|
|
|
if (args[1]) {
|
|
char path[MAX_PATH];
|
|
int i;
|
|
|
|
// ignore the /d option (of the standard cd command)
|
|
|
|
if (_stricmp(args[1], "/d")) {
|
|
i = 1;
|
|
}
|
|
else {
|
|
i = 2;
|
|
}
|
|
|
|
p = args[i];
|
|
|
|
if (*p == '\"' || *p == '\'') {
|
|
|
|
// the parameter is quoted -- remove quotations
|
|
|
|
p++;
|
|
|
|
while (*p && *p != *args[i]) {
|
|
p++;
|
|
}
|
|
|
|
args[i]++; // skip a leading quote
|
|
*p = '\0'; // remove a trailing quote
|
|
}
|
|
else {
|
|
|
|
// the parameter is not quoted
|
|
// -- concatenate params to allow spaces in unquoted path
|
|
|
|
while (i < argc - 1) {
|
|
*(args[i] + strlen(args[i])) = ' ';
|
|
i++;
|
|
}
|
|
}
|
|
|
|
// Match the case of the path to the name on the disk
|
|
|
|
ConvertPathCase(p, path);
|
|
|
|
if (!SetCurrentDirectory(path)) {
|
|
DWORD ret = GetLastError();
|
|
|
|
if (ret == ERROR_FILE_NOT_FOUND) {
|
|
ret = ERROR_PATH_NOT_FOUND;
|
|
}
|
|
|
|
printf("%s", SystemError(ret));
|
|
}
|
|
}
|
|
else {
|
|
if (!GetCurrentDirectory(sizeof(input), input)) {
|
|
printf("%s", SystemError(GetLastError()));
|
|
}
|
|
else {
|
|
printf("%s\n", input);
|
|
}
|
|
}
|
|
}
|
|
else if (isalpha(*args[0]) &&
|
|
*(args[0] + 1) == ':' &&
|
|
*(args[0] + 2) == '\0') {
|
|
|
|
// internal change drive command
|
|
|
|
*args[0] = (char)toupper(*args[0]);
|
|
*(args[0] + 2) = '\\';
|
|
*(args[0] + 3) = '\0';
|
|
|
|
if (!SetCurrentDirectory(args[0])) {
|
|
printf("%s", SystemError(GetLastError()));
|
|
}
|
|
}
|
|
else {
|
|
|
|
// perform the requested VFD command
|
|
|
|
ProcessCommandLine(argc, (const char **)args);
|
|
}
|
|
|
|
printf("\n");
|
|
}
|
|
|
|
return VFD_OK;
|
|
}
|
|
|
|
//
|
|
// process a single command
|
|
//
|
|
int ProcessCommandLine(int argc, const char **args)
|
|
{
|
|
int cmd;
|
|
DWORD ret;
|
|
|
|
//
|
|
// Decide a command to perform
|
|
//
|
|
cmd = ParseCommand(*args);
|
|
|
|
if (cmd < 0) {
|
|
|
|
// no matching command
|
|
|
|
return VFD_NG;
|
|
}
|
|
|
|
if (*(++args) &&
|
|
(!strcmp(*args, "/?") ||
|
|
!_stricmp(*args, "/h"))) {
|
|
|
|
// print a short hint for the command
|
|
|
|
PrintMessage(Commands[cmd].hint);
|
|
return VFD_NG;
|
|
}
|
|
|
|
if (--argc > Commands[cmd].max_args) {
|
|
|
|
// too many parameters for the command
|
|
|
|
PrintMessage(MSG_TOO_MANY_ARGS);
|
|
PrintMessage(Commands[cmd].hint);
|
|
return VFD_NG;
|
|
}
|
|
|
|
// Get the current driver state
|
|
|
|
ret = VfdGetDriverState(&driver_state);
|
|
|
|
if (ret != ERROR_SUCCESS) {
|
|
PrintMessage(MSG_GET_STAT_NG);
|
|
printf("%s", SystemError(ret));
|
|
return VFD_NG;
|
|
}
|
|
|
|
// Perform the requested operation
|
|
|
|
return (*Commands[cmd].func)(args);
|
|
}
|
|
|
|
//
|
|
// Install the Virtual Floppy Driver
|
|
// Command Line Parameters:
|
|
// (optional) driver file path - default to executive's dir
|
|
// (optional) auto start switch - default to demand start
|
|
//
|
|
int Install(const char **args)
|
|
{
|
|
const char *install_path = NULL;
|
|
DWORD start_type = SERVICE_DEMAND_START;
|
|
|
|
DWORD ret;
|
|
|
|
// process parameters
|
|
|
|
while (args && *args) {
|
|
|
|
if (!_stricmp(*args, "/a") ||
|
|
!_stricmp(*args, "/auto")) {
|
|
|
|
if (start_type != SERVICE_DEMAND_START) {
|
|
PrintMessage(MSG_DUPLICATE_ARGS, *args);
|
|
return VFD_NG;
|
|
}
|
|
/*
|
|
if (IS_WINDOWS_NT()) {
|
|
|
|
// On Windows NT, SYSTEM start drivers must be placed
|
|
// under the winnt\system32 directory. Since I don't
|
|
// care to handle driver file copying, I use the AUTO
|
|
// start method for Windows NT.
|
|
|
|
start_type = SERVICE_AUTO_START;
|
|
}
|
|
else {
|
|
|
|
// On Windows XP, the VFD driver must be running when
|
|
// the shell starts -- otherwise the shell doesn't
|
|
// recognize the VFD drives. Since Windows XP allows
|
|
// SYSTEM start drivers to be placed in any local
|
|
// directories, I use the SYSTEM start method here.
|
|
//
|
|
// This is not an issue when the driver is started
|
|
// manually because in that case VFD.EXE and VFDWIN.EXE
|
|
// notify the shell of the VFD drives.
|
|
//
|
|
// On Windows 2000 both SYSTEM and AUTO work fine.
|
|
|
|
start_type = SERVICE_SYSTEM_START;
|
|
}
|
|
*/
|
|
// On second thought -- Win2K / XP mount manager assigns
|
|
// arbitrary drive letters to all drives it finds during
|
|
// the system start up. There is no way to prevent it
|
|
// until the driver is fully PnP compatible, so I'd settle
|
|
// for AUTO start for the time being.
|
|
|
|
start_type = SERVICE_AUTO_START;
|
|
}
|
|
else if (**args == '/') {
|
|
PrintMessage(MSG_UNKNOWN_OPTION, *args);
|
|
PrintMessage(MSG_HINT_INSTALL, help_progname);
|
|
return VFD_NG;
|
|
}
|
|
else {
|
|
if (install_path) {
|
|
PrintMessage(MSG_DUPLICATE_ARGS, "path");
|
|
return VFD_NG;
|
|
}
|
|
|
|
install_path = *args;
|
|
}
|
|
|
|
args++;
|
|
}
|
|
|
|
// already installed?
|
|
|
|
if (driver_state != VFD_NOT_INSTALLED) {
|
|
PrintMessage(MSG_DRIVER_EXISTS);
|
|
return VFD_NG;
|
|
}
|
|
|
|
// install the driver
|
|
|
|
ret = VfdInstallDriver(
|
|
install_path,
|
|
start_type);
|
|
|
|
if (ret != ERROR_SUCCESS) {
|
|
PrintMessage(MSG_INSTALL_NG);
|
|
printf("%s", SystemError(ret));
|
|
return VFD_NG;
|
|
}
|
|
|
|
// Get the latest driver state
|
|
|
|
ret = VfdGetDriverState(&driver_state);
|
|
|
|
if (ret != ERROR_SUCCESS) {
|
|
PrintMessage(MSG_GET_STAT_NG);
|
|
printf("%s", SystemError(ret));
|
|
return VFD_NG;
|
|
}
|
|
|
|
// operation successfull
|
|
|
|
PrintMessage(MSG_INSTALL_OK);
|
|
|
|
return VFD_OK;
|
|
}
|
|
|
|
//
|
|
// Remove Virtual Floppy Driver from system
|
|
// Command Line Parameters:
|
|
// [/F | /FORCE | /Q | /QUIT]
|
|
// /F forces remove operation if the driver cannot be stopped
|
|
// /Q quits remove operation if the driver cannot be stopped
|
|
//
|
|
int Remove(const char **args)
|
|
{
|
|
int mode = OPERATION_ASK;
|
|
const char *stop_params[] = { NULL, NULL };
|
|
DWORD ret;
|
|
int idx;
|
|
|
|
// parse parameters
|
|
|
|
while (args && *args) {
|
|
|
|
if (!_stricmp(*args, "/f") ||
|
|
!_stricmp(*args, "/force")) {
|
|
|
|
if (mode != OPERATION_ASK) {
|
|
PrintMessage(MSG_DUPLICATE_ARGS, *args);
|
|
return VFD_NG;
|
|
}
|
|
|
|
mode = OPERATION_FORCE;
|
|
stop_params[0] = *args;
|
|
}
|
|
else if (!_stricmp(*args, "/q") ||
|
|
!_stricmp(*args, "/quit")) {
|
|
|
|
if (mode != OPERATION_ASK) {
|
|
PrintMessage(MSG_DUPLICATE_ARGS, *args);
|
|
return VFD_NG;
|
|
}
|
|
|
|
mode = OPERATION_QUIT;
|
|
stop_params[0] = *args;
|
|
}
|
|
else {
|
|
PrintMessage(MSG_UNKNOWN_OPTION, *args);
|
|
PrintMessage(MSG_HINT_REMOVE, help_progname);
|
|
return VFD_NG;
|
|
}
|
|
|
|
args++;
|
|
}
|
|
|
|
// ensure the driver is installed
|
|
|
|
if (driver_state == VFD_NOT_INSTALLED) {
|
|
PrintMessage(MSG_NOT_INSTALLED);
|
|
return VFD_NG;
|
|
}
|
|
|
|
// ensure the driver is stopped
|
|
|
|
if (driver_state == SERVICE_RUNNING) {
|
|
|
|
// Try to stop with the same command line option (/F or /Q)
|
|
|
|
while (Stop(stop_params) != VFD_OK) {
|
|
|
|
// stop failed
|
|
|
|
if (mode == OPERATION_FORCE) {
|
|
PrintMessage(MSG_REMOVE_FORCE);
|
|
break;
|
|
}
|
|
else if (mode == OPERATION_QUIT) {
|
|
PrintMessage(MSG_REMOVE_QUIT);
|
|
return VFD_NG;
|
|
}
|
|
else {
|
|
int c;
|
|
|
|
PrintMessage(MSG_REMOVE_WARN);
|
|
|
|
c = InputChar(MSG_RETRY_FORCE_CANCEL, "rfc");
|
|
|
|
if (c == 'f') { // force
|
|
break;
|
|
}
|
|
else if (c == 'c') { // cancel
|
|
return VFD_NG;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// remove the driver
|
|
|
|
ret = VfdRemoveDriver();
|
|
|
|
if (ret != ERROR_SUCCESS) {
|
|
PrintMessage(MSG_REMOVE_NG);
|
|
printf("%s", SystemError(ret));
|
|
return VFD_NG;
|
|
}
|
|
|
|
// Wait for the driver to be actually removed for 3 secs Max.
|
|
|
|
for (idx = 0; idx < 10; idx++) {
|
|
|
|
ret = VfdGetDriverState(&driver_state);
|
|
|
|
if (ret != ERROR_SUCCESS) {
|
|
PrintMessage(MSG_GET_STAT_NG);
|
|
printf("%s", SystemError(ret));
|
|
return VFD_NG;
|
|
}
|
|
|
|
if (driver_state == VFD_NOT_INSTALLED) {
|
|
break;
|
|
}
|
|
|
|
Sleep(300);
|
|
}
|
|
|
|
if (driver_state != VFD_NOT_INSTALLED) {
|
|
PrintMessage(MSG_REMOVE_PENDING);
|
|
return VFD_NG;
|
|
}
|
|
|
|
// operation successful
|
|
|
|
PrintMessage(MSG_REMOVE_OK);
|
|
|
|
return VFD_OK;
|
|
}
|
|
|
|
//
|
|
// Configure the Virtual Floppy Driver
|
|
// Command Line Parameters:
|
|
// /auto, /manual
|
|
//
|
|
int Config(const char **args)
|
|
{
|
|
DWORD start_type = SERVICE_DISABLED;
|
|
DWORD ret;
|
|
|
|
while (args && *args) {
|
|
if (!_stricmp(*args, "/a") ||
|
|
!_stricmp(*args, "/auto")) {
|
|
|
|
if (start_type != SERVICE_DISABLED) {
|
|
PrintMessage(MSG_DUPLICATE_ARGS, *args);
|
|
return VFD_NG;
|
|
}
|
|
|
|
start_type = SERVICE_AUTO_START;
|
|
}
|
|
else if (!_stricmp(*args, "/m") ||
|
|
!_stricmp(*args, "/manual")) {
|
|
|
|
if (start_type != SERVICE_DISABLED) {
|
|
PrintMessage(MSG_DUPLICATE_ARGS, *args);
|
|
return VFD_NG;
|
|
}
|
|
|
|
start_type = SERVICE_DEMAND_START;
|
|
}
|
|
else {
|
|
PrintMessage(MSG_UNKNOWN_OPTION, *args);
|
|
PrintMessage(MSG_HINT_CONFIG, help_progname);
|
|
return VFD_NG;
|
|
}
|
|
|
|
args++;
|
|
}
|
|
|
|
if (start_type == SERVICE_DISABLED) {
|
|
// no parameter is specified
|
|
PrintMessage(MSG_HINT_CONFIG, help_progname);
|
|
return VFD_NG;
|
|
}
|
|
|
|
// ensure that the driver is installed
|
|
|
|
if (driver_state == VFD_NOT_INSTALLED) {
|
|
PrintMessage(MSG_NOT_INSTALLED);
|
|
return VFD_NG;
|
|
}
|
|
|
|
// ensure that the driver is up to date
|
|
|
|
if (CheckDriver() != VFD_OK) {
|
|
return VFD_NG;
|
|
}
|
|
|
|
// configure the driver
|
|
|
|
ret = VfdConfigDriver(start_type);
|
|
|
|
if (ret != ERROR_SUCCESS) {
|
|
PrintMessage(MSG_CONFIG_NG);
|
|
printf("%s", SystemError(ret));
|
|
return VFD_NG;
|
|
}
|
|
|
|
// operation successfull
|
|
|
|
PrintMessage(MSG_CONFIG_OK);
|
|
|
|
return VFD_OK;
|
|
}
|
|
|
|
//
|
|
// Start the Virtual Floppy Driver
|
|
// Command Line Parameters: None
|
|
//
|
|
int Start(const char **args)
|
|
{
|
|
DWORD ret;
|
|
|
|
UNREFERENCED_PARAMETER(args);
|
|
|
|
// ensure that the driver is installed
|
|
|
|
if (driver_state == VFD_NOT_INSTALLED &&
|
|
Install(NULL) != VFD_OK) {
|
|
return VFD_NG;
|
|
}
|
|
|
|
// ensure that the driver is up to date
|
|
|
|
if (CheckDriver() != VFD_OK) {
|
|
return VFD_NG;
|
|
}
|
|
|
|
// ensure that the driver is not running
|
|
|
|
if (driver_state == SERVICE_RUNNING) {
|
|
PrintMessage(MSG_ALREADY_RUNNING);
|
|
return VFD_NG;
|
|
}
|
|
|
|
// start the driver
|
|
|
|
ret = VfdStartDriver(&driver_state);
|
|
|
|
if (ret != ERROR_SUCCESS) {
|
|
PrintMessage(MSG_START_NG);
|
|
printf("%s", SystemError(ret));
|
|
return VFD_NG;
|
|
}
|
|
|
|
// operation successfull
|
|
|
|
PrintMessage(MSG_START_OK);
|
|
|
|
return VFD_OK;
|
|
}
|
|
|
|
//
|
|
// Stop the Virtual Floppy Driver
|
|
// Command Line Parameters:
|
|
// /FORCE | /F Forces the operation on error
|
|
// /QUIT | /Q Quits the operation on error
|
|
//
|
|
int Stop(const char **args)
|
|
{
|
|
int mode = OPERATION_ASK;
|
|
const char *close_params[] = { "*", NULL, NULL };
|
|
DWORD ret;
|
|
|
|
while (args && *args) {
|
|
if (!_stricmp(*args, "/f") ||
|
|
!_stricmp(*args, "/force")) {
|
|
|
|
if (mode != OPERATION_ASK) {
|
|
PrintMessage(MSG_DUPLICATE_ARGS, *args);
|
|
return VFD_NG;
|
|
}
|
|
|
|
mode = OPERATION_FORCE;
|
|
|
|
// parameter to pass to the Close() function
|
|
close_params[1] = *args;
|
|
}
|
|
else if (!_stricmp(*args, "/q") ||
|
|
!_stricmp(*args, "/quit")) {
|
|
|
|
if (mode != OPERATION_ASK) {
|
|
PrintMessage(MSG_DUPLICATE_ARGS, *args);
|
|
return VFD_NG;
|
|
}
|
|
|
|
mode = OPERATION_QUIT;
|
|
|
|
// parameter to pass to the Close() function
|
|
close_params[1] = *args;
|
|
}
|
|
else {
|
|
PrintMessage(MSG_UNKNOWN_OPTION, *args);
|
|
PrintMessage(MSG_HINT_STOP, help_progname);
|
|
return VFD_NG;
|
|
}
|
|
|
|
args++;
|
|
}
|
|
|
|
// ensure that the driver is installed
|
|
|
|
if (driver_state == VFD_NOT_INSTALLED) {
|
|
PrintMessage(MSG_NOT_INSTALLED);
|
|
return VFD_NG;
|
|
}
|
|
|
|
// ensure that the driver is running
|
|
|
|
if (driver_state == SERVICE_STOPPED) {
|
|
PrintMessage(MSG_NOT_STARTED);
|
|
return VFD_NG;
|
|
}
|
|
|
|
// ensure that all drives are empty
|
|
|
|
if (driver_state == SERVICE_RUNNING) {
|
|
|
|
// Try to close drives with the same operation mode (/F or /Q)
|
|
|
|
while (Close(close_params) != VFD_OK) {
|
|
|
|
// close failed
|
|
|
|
if (mode == OPERATION_FORCE) {
|
|
PrintMessage(MSG_STOP_FORCE);
|
|
break;
|
|
}
|
|
else if (mode == OPERATION_QUIT) {
|
|
PrintMessage(MSG_STOP_QUIT);
|
|
return VFD_NG;
|
|
}
|
|
else {
|
|
int c;
|
|
|
|
PrintMessage(MSG_STOP_WARN);
|
|
|
|
c = InputChar(MSG_RETRY_FORCE_CANCEL, "rfc");
|
|
|
|
if (c == 'f') { // force
|
|
break;
|
|
}
|
|
else if (c == 'c') { // cancel
|
|
return VFD_NG;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// stop the driver
|
|
|
|
ret = VfdStopDriver(&driver_state);
|
|
|
|
if (ret != ERROR_SUCCESS) {
|
|
PrintMessage(MSG_STOP_NG);
|
|
printf("%s", SystemError(ret));
|
|
return VFD_NG;
|
|
}
|
|
|
|
if (driver_state != SERVICE_STOPPED) {
|
|
PrintMessage(MSG_STOP_PENDING);
|
|
return VFD_NG;
|
|
}
|
|
|
|
// operation successful
|
|
|
|
PrintMessage(MSG_STOP_OK);
|
|
|
|
return VFD_OK;
|
|
}
|
|
|
|
//
|
|
// Enable / Disable the shell extension
|
|
// Command Line Parameters:
|
|
// (optional) /ON or /OFF
|
|
//
|
|
int Shell(const char **args)
|
|
{
|
|
DWORD ret;
|
|
|
|
ret = VfdCheckHandlers();
|
|
|
|
if (ret != ERROR_SUCCESS &&
|
|
ret != ERROR_PATH_NOT_FOUND &&
|
|
ret != ERROR_FILE_NOT_FOUND) {
|
|
PrintMessage(MSG_GET_SHELLEXT_NG);
|
|
printf("%s", SystemError(ret));
|
|
return VFD_NG;
|
|
}
|
|
|
|
if (args && *args) {
|
|
if (_stricmp(*args, "/on") == 0) {
|
|
if (ret != ERROR_SUCCESS) {
|
|
ret = VfdRegisterHandlers();
|
|
|
|
if (ret != ERROR_SUCCESS) {
|
|
PrintMessage(MSG_SET_SHELLEXT_NG);
|
|
printf("%s", SystemError(ret));
|
|
return VFD_NG;
|
|
}
|
|
}
|
|
}
|
|
else if (_stricmp(*args, "/off") == 0) {
|
|
if (ret == ERROR_SUCCESS) {
|
|
ret = VfdUnregisterHandlers();
|
|
|
|
if (ret != ERROR_SUCCESS) {
|
|
PrintMessage(MSG_SET_SHELLEXT_NG);
|
|
printf("%s", SystemError(ret));
|
|
return VFD_NG;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
PrintMessage(MSG_UNKNOWN_OPTION, *args);
|
|
PrintMessage(MSG_HINT_SHELL, help_progname);
|
|
return VFD_NG;
|
|
}
|
|
|
|
ret = VfdCheckHandlers();
|
|
}
|
|
|
|
if (ret == ERROR_PATH_NOT_FOUND ||
|
|
ret == ERROR_FILE_NOT_FOUND) {
|
|
PrintMessage(MSG_SHELLEXT_DISABLED);
|
|
}
|
|
else if (ret == ERROR_SUCCESS) {
|
|
PrintMessage(MSG_SHELLEXT_ENABLED);
|
|
}
|
|
else {
|
|
PrintMessage(MSG_GET_SHELLEXT_NG);
|
|
printf("%s", SystemError(ret));
|
|
return VFD_NG;
|
|
}
|
|
|
|
return VFD_OK;
|
|
}
|
|
|
|
//
|
|
// Open an image file to a Virtual Floppy Drive
|
|
// Command Line Parameters:
|
|
// [drive:] [file] [/NEW] [/RAM] [/P | /W]
|
|
// [/size] [/media] [/F | /FORCE | /Q | /QUIT]
|
|
|
|
int Open(const char **args)
|
|
{
|
|
int mode = OPERATION_ASK;
|
|
BOOL create = FALSE;
|
|
ULONG target = TARGET_NONE;
|
|
PCSTR file_name = NULL;
|
|
VFD_DISKTYPE disk_type = VFD_DISKTYPE_FILE;
|
|
CHAR protect = '\0';
|
|
VFD_MEDIA media_type = VFD_MEDIA_NONE;
|
|
BOOL five_inch = FALSE;
|
|
VFD_FLAGS media_flags = 0;
|
|
HANDLE hDevice;
|
|
CHAR letter;
|
|
DWORD ret;
|
|
|
|
// process parameters
|
|
|
|
while (args && *args) {
|
|
|
|
if (!_stricmp(*args, "/f") ||
|
|
!_stricmp(*args, "/force")) {
|
|
|
|
if (mode != OPERATION_ASK) {
|
|
PrintMessage(MSG_DUPLICATE_ARGS, *args);
|
|
return VFD_NG;
|
|
}
|
|
|
|
mode = OPERATION_FORCE;
|
|
}
|
|
else if (!_stricmp(*args, "/q") ||
|
|
!_stricmp(*args, "/quit")) {
|
|
|
|
if (mode != OPERATION_ASK) {
|
|
PrintMessage(MSG_DUPLICATE_ARGS, *args);
|
|
return VFD_NG;
|
|
}
|
|
|
|
mode = OPERATION_QUIT;
|
|
}
|
|
|
|
else if (!_stricmp(*args, "/new")) {
|
|
|
|
if (create) {
|
|
PrintMessage(MSG_DUPLICATE_ARGS, *args);
|
|
return VFD_NG;
|
|
}
|
|
|
|
create = TRUE;
|
|
}
|
|
|
|
// Disk type options
|
|
|
|
else if (_stricmp(*args, "/ram") == 0) {
|
|
|
|
if (disk_type != VFD_DISKTYPE_FILE) {
|
|
PrintMessage(MSG_DUPLICATE_ARGS, *args);
|
|
return VFD_NG;
|
|
}
|
|
|
|
disk_type = VFD_DISKTYPE_RAM;
|
|
}
|
|
|
|
// Protect options
|
|
else if (_stricmp(*args, "/p") == 0 ||
|
|
_stricmp(*args, "/w") == 0) {
|
|
|
|
if (protect) {
|
|
PrintMessage(MSG_DUPLICATE_ARGS, *args);
|
|
return VFD_NG;
|
|
}
|
|
|
|
protect = (CHAR)toupper(*(*args + 1));
|
|
}
|
|
|
|
// media size options
|
|
|
|
else if (strcmp(*args, "/160") == 0) {
|
|
if (media_type) {
|
|
PrintMessage(MSG_DUPLICATE_ARGS, *args);
|
|
return VFD_NG;
|
|
}
|
|
|
|
media_type = VFD_MEDIA_F5_160;
|
|
}
|
|
else if (strcmp(*args, "/180") == 0) {
|
|
if (media_type) {
|
|
PrintMessage(MSG_DUPLICATE_ARGS, *args);
|
|
return VFD_NG;
|
|
}
|
|
|
|
media_type = VFD_MEDIA_F5_180;
|
|
}
|
|
else if (strcmp(*args, "/320") == 0) {
|
|
if (media_type) {
|
|
PrintMessage(MSG_DUPLICATE_ARGS, *args);
|
|
return VFD_NG;
|
|
}
|
|
|
|
media_type = VFD_MEDIA_F5_320;
|
|
}
|
|
else if (strcmp(*args, "/360") == 0) {
|
|
if (media_type) {
|
|
PrintMessage(MSG_DUPLICATE_ARGS, *args);
|
|
return VFD_NG;
|
|
}
|
|
|
|
media_type = VFD_MEDIA_F5_360;
|
|
}
|
|
else if (strcmp(*args, "/640") == 0) {
|
|
if (media_type) {
|
|
PrintMessage(MSG_DUPLICATE_ARGS, *args);
|
|
return VFD_NG;
|
|
}
|
|
|
|
media_type = VFD_MEDIA_F3_640;
|
|
}
|
|
else if (strcmp(*args, "/720") == 0) {
|
|
if (media_type) {
|
|
PrintMessage(MSG_DUPLICATE_ARGS, *args);
|
|
return VFD_NG;
|
|
}
|
|
|
|
media_type = VFD_MEDIA_F3_720;
|
|
}
|
|
else if (strcmp(*args, "/820") == 0) {
|
|
if (media_type) {
|
|
PrintMessage(MSG_DUPLICATE_ARGS, *args);
|
|
return VFD_NG;
|
|
}
|
|
|
|
media_type = VFD_MEDIA_F3_820;
|
|
}
|
|
else if (strcmp(*args, "/120") == 0 ||
|
|
strcmp(*args, "/1.20") == 0) {
|
|
if (media_type) {
|
|
PrintMessage(MSG_DUPLICATE_ARGS, *args);
|
|
return VFD_NG;
|
|
}
|
|
|
|
media_type = VFD_MEDIA_F3_1P2;
|
|
}
|
|
else if (strcmp(*args, "/144") == 0 ||
|
|
strcmp(*args, "/1.44") == 0) {
|
|
if (media_type) {
|
|
PrintMessage(MSG_DUPLICATE_ARGS, *args);
|
|
return VFD_NG;
|
|
}
|
|
|
|
media_type = VFD_MEDIA_F3_1P4;
|
|
}
|
|
else if (strcmp(*args, "/168") == 0 ||
|
|
strcmp(*args, "/1.68") == 0) {
|
|
if (media_type) {
|
|
PrintMessage(MSG_DUPLICATE_ARGS, *args);
|
|
return VFD_NG;
|
|
}
|
|
|
|
media_type = VFD_MEDIA_F3_1P6;
|
|
}
|
|
else if (strcmp(*args, "/172") == 0 ||
|
|
strcmp(*args, "/1.72") == 0) {
|
|
if (media_type) {
|
|
PrintMessage(MSG_DUPLICATE_ARGS, *args);
|
|
return VFD_NG;
|
|
}
|
|
|
|
media_type = VFD_MEDIA_F3_1P7;
|
|
}
|
|
else if (strcmp(*args, "/288") == 0 ||
|
|
strcmp(*args, "/2.88") == 0) {
|
|
if (media_type) {
|
|
PrintMessage(MSG_DUPLICATE_ARGS, *args);
|
|
return VFD_NG;
|
|
}
|
|
|
|
media_type = VFD_MEDIA_F3_2P8;
|
|
}
|
|
|
|
// 5.25 inch media
|
|
|
|
else if (strcmp(*args, "/5") == 0 ||
|
|
strcmp(*args, "/525") == 0 ||
|
|
strcmp(*args, "/5.25") == 0) {
|
|
|
|
if (five_inch) {
|
|
PrintMessage(MSG_DUPLICATE_ARGS, *args);
|
|
return VFD_NG;
|
|
}
|
|
|
|
five_inch = TRUE;
|
|
}
|
|
|
|
// target option
|
|
|
|
else if (isalnum(**args) &&
|
|
*(*args + 1) == ':' &&
|
|
*(*args + 2) == '\0') {
|
|
|
|
if (target != TARGET_NONE) {
|
|
PrintMessage(MSG_DUPLICATE_ARGS, *args);
|
|
return VFD_NG;
|
|
}
|
|
|
|
target = toupper(**args);
|
|
}
|
|
|
|
// filename
|
|
|
|
else if (**args != '/') {
|
|
if (file_name) {
|
|
PrintMessage(MSG_DUPLICATE_ARGS, *args);
|
|
return VFD_NG;
|
|
}
|
|
|
|
file_name = *args;
|
|
}
|
|
else {
|
|
PrintMessage(MSG_UNKNOWN_OPTION, *args);
|
|
PrintMessage(MSG_HINT_OPEN, help_progname);
|
|
return VFD_NG;
|
|
}
|
|
|
|
args++;
|
|
}
|
|
|
|
if (target == TARGET_NONE) {
|
|
// default target
|
|
target = '0';
|
|
PrintMessage(MSG_TARGET_NOTICE, target);
|
|
}
|
|
|
|
// check target file
|
|
|
|
if (file_name) {
|
|
DWORD file_attr;
|
|
VFD_FILETYPE file_type;
|
|
ULONG image_size;
|
|
BOOL overwrite = FALSE;
|
|
|
|
ret = VfdCheckImageFile(
|
|
file_name, &file_attr, &file_type, &image_size);
|
|
|
|
if (ret == ERROR_FILE_NOT_FOUND) {
|
|
|
|
// the target file does not exist
|
|
|
|
if (!create) { // create option not specified
|
|
|
|
if (mode == OPERATION_FORCE) {
|
|
PrintMessage(MSG_CREATE_NOTICE);
|
|
}
|
|
else {
|
|
printf("%s", SystemError(ret));
|
|
|
|
if (mode == OPERATION_QUIT ||
|
|
InputChar(MSG_CREATE_CONFIRM, "yn") == 'n') {
|
|
return VFD_NG;
|
|
}
|
|
}
|
|
|
|
create = TRUE;
|
|
}
|
|
}
|
|
else if (ret == ERROR_SUCCESS) {
|
|
|
|
// the target file exists
|
|
|
|
if (create) { // create option is specified
|
|
|
|
if (mode == OPERATION_FORCE) {
|
|
PrintMessage(MSG_OVERWRITE_NOTICE);
|
|
}
|
|
else {
|
|
printf("%s", SystemError(ERROR_FILE_EXISTS));
|
|
|
|
if (mode == OPERATION_QUIT ||
|
|
InputChar(MSG_OVERWRITE_CONFIRM, "yn") == 'n') {
|
|
return VFD_NG;
|
|
}
|
|
}
|
|
|
|
overwrite = TRUE;
|
|
}
|
|
}
|
|
else {
|
|
PrintMessage(MSG_OPEN_NG, file_name);
|
|
printf("%s", SystemError(ret));
|
|
return VFD_NG;
|
|
}
|
|
|
|
//
|
|
// create or overwrite the target file
|
|
//
|
|
|
|
if (create) {
|
|
|
|
if (media_type == VFD_MEDIA_NONE) {
|
|
|
|
if (mode == OPERATION_FORCE) {
|
|
PrintMessage(MSG_CREATE144_NOTICE);
|
|
}
|
|
else {
|
|
PrintMessage(MSG_FILE_MEDIA_UNKNOWN);
|
|
|
|
if (mode == OPERATION_QUIT ||
|
|
InputChar(MSG_CREATE144_CONFIRM, "yn") == 'n') {
|
|
return VFD_NG;
|
|
}
|
|
}
|
|
|
|
media_type = VFD_MEDIA_F3_1P4;
|
|
}
|
|
|
|
ret = VfdCreateImageFile(
|
|
file_name, media_type, VFD_FILETYPE_RAW, overwrite);
|
|
|
|
if (ret != ERROR_SUCCESS) {
|
|
PrintMessage(MSG_CREATE_NG, file_name);
|
|
printf("%s", SystemError(ret));
|
|
return VFD_NG;
|
|
}
|
|
|
|
PrintMessage(MSG_FILE_CREATED);
|
|
|
|
ret = VfdCheckImageFile(
|
|
file_name, &file_attr, &file_type, &image_size);
|
|
|
|
if (ret != ERROR_SUCCESS) {
|
|
PrintMessage(MSG_OPEN_NG, file_name);
|
|
printf("%s", SystemError(ret));
|
|
return VFD_NG;
|
|
}
|
|
}
|
|
else {
|
|
//
|
|
// use the existing target file
|
|
// check image size and the media type
|
|
//
|
|
|
|
VFD_MEDIA def_media; // default media for image size
|
|
ULONG media_size; // specified media size
|
|
|
|
media_size = VfdGetMediaSize(media_type);
|
|
|
|
if (media_size > image_size) {
|
|
|
|
// specified media is too large for the image
|
|
|
|
PrintMessage(MSG_IMAGE_TOO_SMALL);
|
|
return VFD_NG;
|
|
}
|
|
|
|
def_media = VfdLookupMedia(image_size);
|
|
|
|
if (def_media == VFD_MEDIA_NONE) {
|
|
|
|
// image is too small for the smallest media
|
|
|
|
PrintMessage(MSG_IMAGE_TOO_SMALL);
|
|
return VFD_NG;
|
|
}
|
|
|
|
if (media_type == VFD_MEDIA_NONE) {
|
|
|
|
// media type is not specified
|
|
|
|
ULONG def_size = VfdGetMediaSize(def_media);
|
|
|
|
if (def_size != image_size) {
|
|
|
|
// image size does not match the largest media size
|
|
|
|
PrintMessage(MSG_NO_MATCHING_MEDIA, image_size);
|
|
|
|
if (mode == OPERATION_FORCE) {
|
|
PrintMessage(MSG_MEDIATYPE_NOTICE,
|
|
VfdMediaTypeName(def_media), def_size);
|
|
}
|
|
else if (mode == OPERATION_QUIT) {
|
|
return VFD_NG;
|
|
}
|
|
else {
|
|
PrintMessage(MSG_MEDIATYPE_SUGGEST,
|
|
VfdMediaTypeName(def_media), def_size);
|
|
|
|
if (InputChar(MSG_MEDIATYPE_CONFIRM, "yn") == 'n') {
|
|
return VFD_NG;
|
|
}
|
|
}
|
|
}
|
|
|
|
media_type = def_media;
|
|
}
|
|
}
|
|
|
|
// check file attributes against the disk type
|
|
|
|
if (file_type == VFD_FILETYPE_ZIP ||
|
|
(file_attr & (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_COMPRESSED | FILE_ATTRIBUTE_ENCRYPTED))) {
|
|
|
|
if (disk_type != VFD_DISKTYPE_RAM) {
|
|
|
|
if (mode == OPERATION_FORCE) {
|
|
PrintMessage(MSG_RAM_MODE_NOTICE);
|
|
}
|
|
else {
|
|
PrintMessage(MSG_RAM_MODE_ONLY);
|
|
|
|
if (mode == OPERATION_QUIT ||
|
|
InputChar(MSG_RAM_MODE_CONFIRM, "yn") == 'n') {
|
|
return VFD_NG;
|
|
}
|
|
}
|
|
|
|
disk_type = VFD_DISKTYPE_RAM;
|
|
}
|
|
}
|
|
|
|
if (disk_type != VFD_DISKTYPE_FILE) {
|
|
if (!protect) {
|
|
PrintMessage(MSG_DEFAULT_PROTECT);
|
|
protect = 'P';
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
//
|
|
// pure RAM disk
|
|
//
|
|
disk_type = VFD_DISKTYPE_RAM;
|
|
|
|
if (media_type == VFD_MEDIA_NONE) {
|
|
|
|
if (mode == OPERATION_FORCE) {
|
|
PrintMessage(MSG_CREATE144_NOTICE);
|
|
}
|
|
else {
|
|
PrintMessage(MSG_RAM_MEDIA_UNKNOWN);
|
|
|
|
if (mode == OPERATION_QUIT ||
|
|
InputChar(MSG_CREATE144_CONFIRM, "yn") == 'n') {
|
|
return VFD_NG;
|
|
}
|
|
}
|
|
|
|
media_type = VFD_MEDIA_F3_1P4;
|
|
}
|
|
}
|
|
|
|
if (protect == 'P') {
|
|
media_flags |= VFD_FLAG_WRITE_PROTECTED;
|
|
}
|
|
|
|
if (five_inch &&
|
|
VfdGetMediaSize(media_type) ==
|
|
VfdGetMediaSize((VFD_MEDIA)(media_type + 1))) {
|
|
media_type = (VFD_MEDIA)(media_type + 1);
|
|
}
|
|
|
|
// ensure that the driver is installed
|
|
|
|
if (driver_state == VFD_NOT_INSTALLED &&
|
|
Install(NULL) != VFD_OK) {
|
|
return VFD_NG;
|
|
}
|
|
|
|
// ensure that the driver is up to date
|
|
|
|
if (CheckDriver() != VFD_OK) {
|
|
return VFD_NG;
|
|
}
|
|
|
|
// ensure that the driver is running
|
|
|
|
if (driver_state != SERVICE_RUNNING &&
|
|
Start(NULL) != VFD_OK) {
|
|
return VFD_NG;
|
|
}
|
|
|
|
// Open the target device
|
|
|
|
hDevice = VfdOpenDevice(target);
|
|
|
|
if (hDevice == INVALID_HANDLE_VALUE) {
|
|
ret = GetLastError();
|
|
PrintMessage(MSG_ACCESS_NG, target);
|
|
printf("%s", SystemError(ret));
|
|
return VFD_NG;
|
|
}
|
|
|
|
// Ensure that the drive is empty
|
|
|
|
ret = VfdGetMediaState(hDevice);
|
|
|
|
if (ret != ERROR_NOT_READY) {
|
|
if (ret == ERROR_SUCCESS ||
|
|
ret == ERROR_WRITE_PROTECT) {
|
|
PrintMessage(MSG_DRIVE_BUSY);
|
|
}
|
|
else {
|
|
PrintMessage(MSG_GET_MEDIA_NG);
|
|
printf("%s", SystemError(ret));
|
|
}
|
|
|
|
CloseHandle(hDevice);
|
|
return VFD_NG;
|
|
}
|
|
|
|
// Open the image file
|
|
|
|
ret = VfdOpenImage(hDevice, file_name,
|
|
disk_type, media_type, media_flags);
|
|
|
|
if (ret != ERROR_SUCCESS) {
|
|
PrintMessage(MSG_OPEN_NG, file_name ? file_name : "<RAM>");
|
|
printf("%s", SystemError(ret));
|
|
|
|
CloseHandle(hDevice);
|
|
return VFD_NG;
|
|
}
|
|
|
|
// assign a drive letter if the drive has none
|
|
|
|
VfdGetGlobalLink(hDevice, &letter);
|
|
|
|
if (!isalpha(letter)) {
|
|
VfdGetLocalLink(hDevice, &letter);
|
|
}
|
|
|
|
if (!isalpha(letter)) {
|
|
VfdSetLocalLink(hDevice, VfdChooseLetter());
|
|
}
|
|
|
|
// Get the actually opened image information.
|
|
|
|
PrintImageInfo(hDevice);
|
|
|
|
CloseHandle(hDevice);
|
|
|
|
return VFD_OK;
|
|
}
|
|
|
|
//
|
|
// Close the current virtual floppy image
|
|
// Command Line Parameters:
|
|
// drive number or drive letter
|
|
// /F | /FORCE | /Q | /QUIT
|
|
//
|
|
int Close(const char **args)
|
|
{
|
|
ULONG mode = OPERATION_ASK;
|
|
|
|
ULONG target_min = TARGET_NONE;
|
|
ULONG target_max = TARGET_NONE;
|
|
HANDLE hDevice;
|
|
|
|
VFD_MEDIA media_type;
|
|
VFD_FLAGS media_flags;
|
|
|
|
DWORD ret;
|
|
|
|
// check parameterS
|
|
|
|
while (args && *args) {
|
|
|
|
if (!_stricmp(*args, "/f") ||
|
|
!_stricmp(*args, "/force")) {
|
|
|
|
if (mode != OPERATION_ASK) {
|
|
PrintMessage(MSG_DUPLICATE_ARGS, *args);
|
|
return VFD_NG;
|
|
}
|
|
|
|
mode = OPERATION_FORCE;
|
|
}
|
|
else if (!_stricmp(*args, "/q") ||
|
|
!_stricmp(*args, "/quit")) {
|
|
|
|
if (mode != OPERATION_ASK) {
|
|
PrintMessage(MSG_DUPLICATE_ARGS, *args);
|
|
return VFD_NG;
|
|
}
|
|
|
|
mode = OPERATION_QUIT;
|
|
}
|
|
else if ((isalnum(**args) || **args == '*') &&
|
|
(*(*args + 1) == ':' || *(*args + 1) == '\0')) {
|
|
|
|
if (target_min != TARGET_NONE) {
|
|
PrintMessage(MSG_DUPLICATE_ARGS, *args);
|
|
return VFD_NG;
|
|
}
|
|
|
|
if (**args == '*') {
|
|
target_min = '0';
|
|
target_max = '0' + VFD_MAXIMUM_DEVICES;
|
|
}
|
|
else {
|
|
target_min = toupper(**args);
|
|
target_max = target_min + 1;
|
|
}
|
|
}
|
|
else {
|
|
PrintMessage(MSG_UNKNOWN_OPTION, *args);
|
|
PrintMessage(MSG_HINT_CLOSE, help_progname);
|
|
return VFD_NG;
|
|
}
|
|
|
|
args++;
|
|
}
|
|
|
|
if (target_min == TARGET_NONE) {
|
|
// default target = drive 0
|
|
target_min = '0';
|
|
target_max = '1';
|
|
PrintMessage(MSG_TARGET_NOTICE, target_min);
|
|
}
|
|
|
|
// ensure that the driver is installed
|
|
|
|
if (driver_state == VFD_NOT_INSTALLED) {
|
|
PrintMessage(MSG_NOT_INSTALLED);
|
|
return VFD_NG;
|
|
}
|
|
|
|
// ensure that the driver is running
|
|
|
|
if (driver_state != SERVICE_RUNNING) {
|
|
PrintMessage(MSG_NOT_STARTED);
|
|
return VFD_NG;
|
|
}
|
|
|
|
// Close the drive(s)
|
|
|
|
while (target_min < target_max) {
|
|
|
|
// open the target device
|
|
|
|
hDevice = VfdOpenDevice(target_min);
|
|
|
|
if (hDevice == INVALID_HANDLE_VALUE) {
|
|
ret = GetLastError();
|
|
|
|
PrintMessage(MSG_ACCESS_NG, target_min);
|
|
printf("%s", SystemError(ret));
|
|
|
|
if (mode != OPERATION_FORCE) {
|
|
return VFD_NG;
|
|
}
|
|
|
|
target_min++;
|
|
continue;
|
|
}
|
|
|
|
// get the current image information
|
|
|
|
ret = VfdGetImageInfo(hDevice, NULL, NULL,
|
|
&media_type, &media_flags, NULL, NULL);
|
|
|
|
if (ret != ERROR_SUCCESS) {
|
|
PrintMessage(MSG_ACCESS_NG, target_min);
|
|
printf("%s", SystemError(ret));
|
|
|
|
CloseHandle(hDevice);
|
|
|
|
if (mode != OPERATION_FORCE) {
|
|
return VFD_NG;
|
|
}
|
|
|
|
target_min++;
|
|
continue;
|
|
}
|
|
|
|
if (media_type == VFD_MEDIA_NONE) {
|
|
|
|
// drive is empty
|
|
|
|
CloseHandle(hDevice);
|
|
target_min++;
|
|
continue;
|
|
}
|
|
|
|
if (media_flags & VFD_FLAG_DATA_MODIFIED) {
|
|
|
|
// RAM disk data is modified
|
|
|
|
PrintMessage(MSG_MEDIA_MODIFIED, target_min);
|
|
|
|
if (mode == OPERATION_FORCE) {
|
|
PrintMessage(MSG_CLOSE_FORCE);
|
|
}
|
|
else if (mode == OPERATION_QUIT) {
|
|
PrintMessage(MSG_CLOSE_QUIT);
|
|
CloseHandle(hDevice);
|
|
return VFD_NG;
|
|
}
|
|
else {
|
|
if (InputChar(MSG_CLOSE_CONFIRM, "yn") == 'n') {
|
|
CloseHandle(hDevice);
|
|
return VFD_NG;
|
|
}
|
|
}
|
|
}
|
|
|
|
retry:
|
|
ret = VfdCloseImage(
|
|
hDevice, (mode == OPERATION_FORCE));
|
|
|
|
if (ret == ERROR_ACCESS_DENIED) {
|
|
|
|
PrintMessage(MSG_LOCK_NG, target_min);
|
|
|
|
if (mode == OPERATION_QUIT) {
|
|
CloseHandle(hDevice);
|
|
return VFD_NG;
|
|
}
|
|
else if (mode == OPERATION_ASK) {
|
|
|
|
int c;
|
|
|
|
if (IS_WINDOWS_NT()) {
|
|
c = InputChar(MSG_RETRY_CANCEL, "rc");
|
|
}
|
|
else {
|
|
c = InputChar(MSG_RETRY_FORCE_CANCEL, "rfc");
|
|
}
|
|
|
|
if (c == 'f') { // force
|
|
ret = VfdCloseImage(hDevice, TRUE);
|
|
}
|
|
else if (c == 'c') { // cancel
|
|
CloseHandle(hDevice);
|
|
return VFD_NG;
|
|
}
|
|
else {
|
|
goto retry;
|
|
}
|
|
}
|
|
}
|
|
|
|
CloseHandle(hDevice);
|
|
|
|
if (ret == ERROR_SUCCESS) {
|
|
PrintMessage(MSG_CLOSE_OK, target_min);
|
|
}
|
|
else if (ret != ERROR_NOT_READY) {
|
|
PrintMessage(MSG_CLOSE_NG, target_min);
|
|
printf("%s", SystemError(ret));
|
|
|
|
if (mode != OPERATION_FORCE) {
|
|
return VFD_NG;
|
|
}
|
|
}
|
|
|
|
target_min++;
|
|
}
|
|
|
|
return VFD_OK;
|
|
}
|
|
|
|
//
|
|
// Save the current image into a file
|
|
//
|
|
int Save(const char **args)
|
|
{
|
|
int mode = OPERATION_ASK;
|
|
ULONG target = TARGET_NONE;
|
|
CHAR file_name[MAX_PATH] = {0};
|
|
BOOL overwrite = FALSE;
|
|
BOOL truncate = FALSE;
|
|
|
|
HANDLE hDevice;
|
|
CHAR current[MAX_PATH] = {0};
|
|
VFD_MEDIA media_type;
|
|
VFD_FLAGS media_flags;
|
|
VFD_FILETYPE file_type;
|
|
DWORD file_attr;
|
|
ULONG image_size;
|
|
DWORD ret;
|
|
|
|
// check parameters
|
|
|
|
while (args && *args) {
|
|
|
|
if (!_stricmp(*args, "/f") ||
|
|
!_stricmp(*args, "/force")) {
|
|
|
|
if (mode != OPERATION_ASK) {
|
|
PrintMessage(MSG_DUPLICATE_ARGS, *args);
|
|
return VFD_NG;
|
|
}
|
|
|
|
mode = OPERATION_FORCE;
|
|
}
|
|
else if (!_stricmp(*args, "/q") ||
|
|
!_stricmp(*args, "/quit")) {
|
|
|
|
if (mode != OPERATION_ASK) {
|
|
PrintMessage(MSG_DUPLICATE_ARGS, *args);
|
|
return VFD_NG;
|
|
}
|
|
|
|
mode = OPERATION_QUIT;
|
|
}
|
|
else if (!_stricmp(*args, "/o") ||
|
|
!_stricmp(*args, "/over")) {
|
|
|
|
if (truncate || overwrite) {
|
|
PrintMessage(MSG_DUPLICATE_ARGS, *args);
|
|
return VFD_NG;
|
|
}
|
|
|
|
overwrite = TRUE;
|
|
}
|
|
else if (!_stricmp(*args, "/t") ||
|
|
!_stricmp(*args, "/trunc")) {
|
|
|
|
if (truncate || overwrite) {
|
|
PrintMessage(MSG_DUPLICATE_ARGS, *args);
|
|
return VFD_NG;
|
|
}
|
|
|
|
truncate = TRUE;
|
|
}
|
|
else if (isalnum(**args) &&
|
|
*(*args + 1) == ':' &&
|
|
*(*args + 2) == '\0') {
|
|
|
|
if (target != TARGET_NONE) {
|
|
PrintMessage(MSG_DUPLICATE_ARGS, *args);
|
|
return VFD_NG;
|
|
}
|
|
|
|
target = toupper(**args);
|
|
}
|
|
else if (**args == '/') {
|
|
PrintMessage(MSG_UNKNOWN_OPTION, *args);
|
|
PrintMessage(MSG_HINT_SAVE, help_progname);
|
|
return VFD_NG;
|
|
}
|
|
else {
|
|
strcpy(file_name, *args);
|
|
}
|
|
|
|
args++;
|
|
}
|
|
|
|
if (target == TARGET_NONE) {
|
|
target = '0';
|
|
PrintMessage(MSG_TARGET_NOTICE, target);
|
|
}
|
|
|
|
// ensure that the driver is installed
|
|
|
|
if (driver_state == VFD_NOT_INSTALLED) {
|
|
PrintMessage(MSG_NOT_INSTALLED);
|
|
return VFD_NG;
|
|
}
|
|
|
|
// ensure that the driver is up to date
|
|
|
|
if (CheckDriver() != VFD_OK) {
|
|
return VFD_NG;
|
|
}
|
|
|
|
// ensure that the driver is running
|
|
|
|
if (driver_state != SERVICE_RUNNING) {
|
|
PrintMessage(MSG_NOT_STARTED);
|
|
return VFD_NG;
|
|
}
|
|
|
|
// Open the target device
|
|
|
|
hDevice = VfdOpenDevice(target);
|
|
|
|
if (hDevice == INVALID_HANDLE_VALUE) {
|
|
ret = GetLastError();
|
|
PrintMessage(MSG_ACCESS_NG, target);
|
|
printf("%s", SystemError(ret));
|
|
return VFD_NG;
|
|
}
|
|
|
|
// Get the current image info
|
|
|
|
ret = VfdGetImageInfo(hDevice, current, NULL,
|
|
&media_type, &media_flags, NULL, NULL);
|
|
|
|
if (ret != ERROR_SUCCESS) {
|
|
printf("%s", SystemError(ret));
|
|
CloseHandle(hDevice);
|
|
return VFD_NG;
|
|
}
|
|
|
|
if (media_type == VFD_MEDIA_NONE) {
|
|
printf("%s", SystemError(ERROR_NOT_READY));
|
|
CloseHandle(hDevice);
|
|
return VFD_NG;
|
|
}
|
|
|
|
if (file_name[0] == '\0') {
|
|
|
|
if (current[0] == '\0') {
|
|
|
|
PrintMessage(MSG_TARGET_REQUIRED);
|
|
CloseHandle(hDevice);
|
|
|
|
return VFD_NG;
|
|
}
|
|
|
|
strcpy(file_name, current);
|
|
}
|
|
|
|
if (!_stricmp(file_name, current)) {
|
|
|
|
// target is the current image file
|
|
|
|
if (!(media_flags & VFD_FLAG_DATA_MODIFIED)) {
|
|
|
|
// FILE disk (always up to date) or RAM disk is not modified
|
|
|
|
PrintMessage(MSG_TARGET_UP_TO_DATE);
|
|
CloseHandle(hDevice);
|
|
|
|
return VFD_OK;
|
|
}
|
|
|
|
overwrite = TRUE;
|
|
}
|
|
|
|
// check target file
|
|
|
|
ret = VfdCheckImageFile(file_name,
|
|
&file_attr, &file_type, &image_size);
|
|
|
|
if (ret == ERROR_SUCCESS) {
|
|
|
|
if (!overwrite && !truncate) {
|
|
|
|
if (mode == OPERATION_FORCE) {
|
|
PrintMessage(MSG_OVERWRITE_NOTICE);
|
|
overwrite = TRUE;
|
|
}
|
|
else if (mode == OPERATION_QUIT) {
|
|
printf("%s", SystemError(ERROR_FILE_EXISTS));
|
|
CloseHandle(hDevice);
|
|
|
|
return VFD_NG;
|
|
}
|
|
else {
|
|
int c;
|
|
|
|
printf("%s", SystemError(ERROR_FILE_EXISTS));
|
|
|
|
c = InputChar(MSG_OVERWRITE_PROMPT, "otc");
|
|
|
|
if (c == 'o') {
|
|
overwrite = TRUE;
|
|
}
|
|
else if (c == 't') {
|
|
truncate = TRUE;
|
|
}
|
|
else {
|
|
CloseHandle(hDevice);
|
|
return VFD_NG;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (ret != ERROR_FILE_NOT_FOUND) {
|
|
|
|
printf("%s", SystemError(ret));
|
|
CloseHandle(hDevice);
|
|
|
|
return VFD_NG;
|
|
}
|
|
|
|
if (file_type == VFD_FILETYPE_ZIP) {
|
|
|
|
// Cannot update a zip file
|
|
|
|
PrintMessage(MSG_TARGET_IS_ZIP);
|
|
CloseHandle(hDevice);
|
|
|
|
return VFD_NG;
|
|
}
|
|
|
|
retry:
|
|
ret = VfdDismountVolume(
|
|
hDevice, (mode == OPERATION_FORCE));
|
|
|
|
if (ret == ERROR_ACCESS_DENIED) {
|
|
|
|
PrintMessage(MSG_LOCK_NG, target);
|
|
|
|
if (mode == OPERATION_FORCE) {
|
|
PrintMessage(MSG_SAVE_FORCE);
|
|
}
|
|
else if (mode == OPERATION_QUIT) {
|
|
PrintMessage(MSG_SAVE_QUIT);
|
|
CloseHandle(hDevice);
|
|
return VFD_NG;
|
|
}
|
|
else {
|
|
int c = InputChar(MSG_RETRY_FORCE_CANCEL, "rfc");
|
|
|
|
if (c == 'r') { // retry
|
|
goto retry;
|
|
}
|
|
else if (c == 'f') { // force
|
|
VfdDismountVolume(hDevice, TRUE);
|
|
}
|
|
else { // cancel
|
|
CloseHandle(hDevice);
|
|
return VFD_NG;
|
|
}
|
|
}
|
|
}
|
|
else if (ret != ERROR_SUCCESS) {
|
|
printf("%s", SystemError(ret));
|
|
CloseHandle(hDevice);
|
|
return VFD_NG;
|
|
}
|
|
|
|
ret = VfdSaveImage(hDevice, file_name,
|
|
(overwrite || truncate), truncate);
|
|
|
|
CloseHandle(hDevice);
|
|
|
|
if (ret != ERROR_SUCCESS) {
|
|
PrintMessage(MSG_SAVE_NG, target, file_name);
|
|
printf("%s", SystemError(ret));
|
|
|
|
return VFD_NG;
|
|
}
|
|
|
|
PrintMessage(MSG_SAVE_OK, target, file_name);
|
|
|
|
return VFD_OK;
|
|
}
|
|
|
|
//
|
|
// Enable/disable virtual media write protection
|
|
//
|
|
int Protect(const char **args)
|
|
{
|
|
#define PROTECT_NONE 0
|
|
#define PROTECT_ON 1
|
|
#define PROTECT_OFF 2
|
|
ULONG protect = PROTECT_NONE;
|
|
ULONG target = TARGET_NONE;
|
|
HANDLE hDevice;
|
|
DWORD ret;
|
|
|
|
// check parameters
|
|
|
|
while (args && *args) {
|
|
|
|
// Disk type options
|
|
|
|
if (_stricmp(*args, "/on") == 0) {
|
|
|
|
if (protect) {
|
|
PrintMessage(MSG_DUPLICATE_ARGS, *args);
|
|
return VFD_NG;
|
|
}
|
|
|
|
protect = PROTECT_ON;
|
|
}
|
|
else if (_stricmp(*args, "/off") == 0) {
|
|
|
|
if (protect) {
|
|
PrintMessage(MSG_DUPLICATE_ARGS, *args);
|
|
return VFD_NG;
|
|
}
|
|
|
|
protect = PROTECT_OFF;
|
|
}
|
|
else if (isalnum(**args)) {
|
|
|
|
if (target != TARGET_NONE) {
|
|
PrintMessage(MSG_DUPLICATE_ARGS, *args);
|
|
return VFD_NG;
|
|
}
|
|
|
|
target = toupper(**args);
|
|
}
|
|
else {
|
|
PrintMessage(MSG_UNKNOWN_OPTION, *args);
|
|
PrintMessage(MSG_HINT_PROTECT, help_progname);
|
|
return VFD_NG;
|
|
}
|
|
|
|
args++;
|
|
}
|
|
|
|
if (target == TARGET_NONE) {
|
|
target = '0';
|
|
PrintMessage(MSG_TARGET_NOTICE, target);
|
|
}
|
|
|
|
// ensure that the driver is installed
|
|
|
|
if (driver_state == VFD_NOT_INSTALLED) {
|
|
PrintMessage(MSG_NOT_INSTALLED);
|
|
return VFD_NG;
|
|
}
|
|
|
|
// ensure that the driver is up to date
|
|
|
|
if (CheckDriver() != VFD_OK) {
|
|
return VFD_NG;
|
|
}
|
|
|
|
// ensure that the driver is running
|
|
|
|
if (driver_state != SERVICE_RUNNING) {
|
|
PrintMessage(MSG_NOT_STARTED);
|
|
return VFD_NG;
|
|
}
|
|
|
|
// open the target drive
|
|
|
|
hDevice = VfdOpenDevice(target);
|
|
|
|
if (hDevice == INVALID_HANDLE_VALUE) {
|
|
ret = GetLastError();
|
|
PrintMessage(MSG_ACCESS_NG, target);
|
|
printf("%s", SystemError(ret));
|
|
return VFD_NG;
|
|
}
|
|
|
|
if (protect) {
|
|
// change protect state
|
|
|
|
ret = VfdWriteProtect(
|
|
hDevice, (protect == PROTECT_ON));
|
|
|
|
if (ret != ERROR_SUCCESS) {
|
|
PrintMessage(MSG_PROTECT_NG, target);
|
|
printf("%s", SystemError(ret));
|
|
|
|
CloseHandle(hDevice);
|
|
return VFD_NG;
|
|
}
|
|
}
|
|
|
|
// get the current protect state
|
|
|
|
ret = VfdGetMediaState(hDevice);
|
|
|
|
CloseHandle(hDevice);
|
|
|
|
if (ret == ERROR_SUCCESS) {
|
|
PrintMessage(MSG_MEDIA_WRITABLE);
|
|
}
|
|
else if (ret == ERROR_WRITE_PROTECT) {
|
|
PrintMessage(MSG_MEDIA_PROTECTED);
|
|
}
|
|
else {
|
|
PrintMessage(MSG_GET_MEDIA_NG);
|
|
printf("%s", SystemError(ret));
|
|
return VFD_NG;
|
|
}
|
|
|
|
return VFD_OK;
|
|
}
|
|
|
|
//
|
|
// Format the virtual media with FAT12
|
|
//
|
|
int Format(const char **args)
|
|
{
|
|
int mode = OPERATION_ASK;
|
|
ULONG target = TARGET_NONE;
|
|
HANDLE hDevice;
|
|
DWORD ret;
|
|
|
|
// check parameters
|
|
|
|
while (args && *args) {
|
|
|
|
if (!_stricmp(*args, "/f") ||
|
|
!_stricmp(*args, "/force")) {
|
|
|
|
if (mode != OPERATION_ASK) {
|
|
PrintMessage(MSG_DUPLICATE_ARGS, *args);
|
|
return VFD_NG;
|
|
}
|
|
|
|
mode = OPERATION_FORCE;
|
|
}
|
|
else if (!_stricmp(*args, "/q") ||
|
|
!_stricmp(*args, "/quit")) {
|
|
|
|
if (mode != OPERATION_ASK) {
|
|
PrintMessage(MSG_DUPLICATE_ARGS, *args);
|
|
return VFD_NG;
|
|
}
|
|
|
|
mode = OPERATION_QUIT;
|
|
}
|
|
else if (isalnum(**args)) {
|
|
if (target != TARGET_NONE) {
|
|
PrintMessage(MSG_DUPLICATE_ARGS, *args);
|
|
return VFD_NG;
|
|
}
|
|
|
|
target = toupper(**args);
|
|
}
|
|
else {
|
|
PrintMessage(MSG_UNKNOWN_OPTION, *args);
|
|
PrintMessage(MSG_HINT_FORMAT, help_progname);
|
|
return VFD_NG;
|
|
}
|
|
|
|
args++;
|
|
}
|
|
|
|
if (target == TARGET_NONE) {
|
|
target = '0';
|
|
PrintMessage(MSG_TARGET_NOTICE, target);
|
|
}
|
|
|
|
// ensure that the driver is installed
|
|
|
|
if (driver_state == VFD_NOT_INSTALLED) {
|
|
PrintMessage(MSG_NOT_INSTALLED);
|
|
return VFD_NG;
|
|
}
|
|
|
|
// ensure that the driver is up to date
|
|
|
|
if (CheckDriver() != VFD_OK) {
|
|
return VFD_NG;
|
|
}
|
|
|
|
// ensure that the driver is running
|
|
|
|
if (driver_state != SERVICE_RUNNING) {
|
|
PrintMessage(MSG_NOT_STARTED);
|
|
return VFD_NG;
|
|
}
|
|
|
|
// Open the device
|
|
|
|
hDevice = VfdOpenDevice(target);
|
|
|
|
if (hDevice == INVALID_HANDLE_VALUE) {
|
|
ret = GetLastError();
|
|
PrintMessage(MSG_ACCESS_NG, target);
|
|
printf("%s", SystemError(ret));
|
|
return VFD_NG;
|
|
}
|
|
|
|
// check if the media is writable
|
|
|
|
ret = VfdGetMediaState(hDevice);
|
|
|
|
if (ret != ERROR_SUCCESS) {
|
|
PrintMessage(MSG_FORMAT_NG, target);
|
|
printf("%s", SystemError(ret));
|
|
|
|
CloseHandle(hDevice);
|
|
return VFD_NG;
|
|
}
|
|
|
|
// format the media
|
|
|
|
retry:
|
|
ret = VfdDismountVolume(
|
|
hDevice, (mode == OPERATION_FORCE));
|
|
|
|
if (ret == ERROR_ACCESS_DENIED) {
|
|
|
|
PrintMessage(MSG_LOCK_NG, target);
|
|
|
|
if (mode == OPERATION_FORCE) {
|
|
PrintMessage(MSG_FORMAT_FORCE);
|
|
}
|
|
else if (mode == OPERATION_QUIT) {
|
|
PrintMessage(MSG_FORMAT_QUIT);
|
|
CloseHandle(hDevice);
|
|
return VFD_NG;
|
|
}
|
|
else {
|
|
int c = InputChar(MSG_RETRY_FORCE_CANCEL, "rfc");
|
|
|
|
if (c == 'r') { // retry
|
|
goto retry;
|
|
}
|
|
else if (c == 'f') { // force
|
|
VfdDismountVolume(hDevice, TRUE);
|
|
}
|
|
else { // cancel
|
|
CloseHandle(hDevice);
|
|
return VFD_NG;
|
|
}
|
|
}
|
|
}
|
|
else if (ret != ERROR_SUCCESS) {
|
|
PrintMessage(MSG_LOCK_NG, target);
|
|
CloseHandle(hDevice);
|
|
return VFD_NG;
|
|
}
|
|
|
|
ret = VfdFormatMedia(hDevice);
|
|
|
|
CloseHandle(hDevice);
|
|
|
|
if (ret != ERROR_SUCCESS) {
|
|
PrintMessage(MSG_FORMAT_NG, target);
|
|
printf("%s", SystemError(ret));
|
|
return VFD_NG;
|
|
}
|
|
|
|
// successful operation
|
|
|
|
PrintMessage(MSG_FORMAT_OK);
|
|
|
|
return VFD_OK;
|
|
}
|
|
|
|
//
|
|
// Assign a drive letter to a Virtual Floppy Drive
|
|
//
|
|
int Link(const char **args)
|
|
{
|
|
ULONG target_min = TARGET_NONE;
|
|
ULONG target_max = TARGET_NONE;
|
|
PCSTR letters = NULL;
|
|
BOOL global = TRUE;
|
|
HANDLE hDevice;
|
|
DWORD ret;
|
|
|
|
while (args && *args) {
|
|
if (!_stricmp(*args, "/g")) {
|
|
global = TRUE;
|
|
}
|
|
else if (!_stricmp(*args, "/l")) {
|
|
global = FALSE;
|
|
}
|
|
else if (isdigit(**args) || **args == '*') {
|
|
if (target_min != TARGET_NONE) {
|
|
PrintMessage(MSG_DUPLICATE_ARGS, *args);
|
|
return VFD_NG;
|
|
}
|
|
|
|
if (**args == '*') {
|
|
target_min = '0';
|
|
target_max = '0' + VFD_MAXIMUM_DEVICES;
|
|
}
|
|
else {
|
|
target_min = **args;
|
|
target_max = target_min + 1;
|
|
}
|
|
}
|
|
else if (isalpha(**args)) {
|
|
if (letters) {
|
|
PrintMessage(MSG_DUPLICATE_ARGS, *args);
|
|
return VFD_NG;
|
|
}
|
|
letters = *args;
|
|
}
|
|
else {
|
|
PrintMessage(MSG_UNKNOWN_OPTION, *args);
|
|
PrintMessage(MSG_HINT_LINK, help_progname);
|
|
return VFD_NG;
|
|
}
|
|
|
|
args++;
|
|
}
|
|
|
|
if (target_min == TARGET_NONE) {
|
|
// default: drive 0
|
|
target_min = '0';
|
|
target_max = '1';
|
|
PrintMessage(MSG_TARGET_NOTICE, target_min);
|
|
}
|
|
|
|
// ensure that the driver is installed
|
|
|
|
if (driver_state == VFD_NOT_INSTALLED) {
|
|
PrintMessage(MSG_NOT_INSTALLED);
|
|
return VFD_NG;
|
|
}
|
|
|
|
// ensure that the driver is up to date
|
|
|
|
if (CheckDriver() != VFD_OK) {
|
|
return VFD_NG;
|
|
}
|
|
|
|
// ensure that the driver is running
|
|
|
|
if (driver_state != SERVICE_RUNNING) {
|
|
PrintMessage(MSG_NOT_STARTED);
|
|
return VFD_NG;
|
|
}
|
|
|
|
while (target_min < target_max) {
|
|
ULONG number;
|
|
CHAR letter;
|
|
|
|
hDevice = VfdOpenDevice(target_min);
|
|
|
|
if (hDevice == INVALID_HANDLE_VALUE) {
|
|
ret = GetLastError();
|
|
PrintMessage(MSG_ACCESS_NG, target_min);
|
|
printf("%s", SystemError(ret));
|
|
target_min++;
|
|
continue;
|
|
}
|
|
|
|
ret = VfdGetDeviceNumber(hDevice, &number);
|
|
|
|
if (ret != ERROR_SUCCESS) {
|
|
PrintMessage(MSG_ACCESS_NG, target_min);
|
|
printf("%s", SystemError(ret));
|
|
CloseHandle(hDevice);
|
|
target_min++;
|
|
continue;
|
|
}
|
|
|
|
if (letters && isalpha(*letters)) {
|
|
letter = (CHAR)toupper(*(letters++));
|
|
}
|
|
else {
|
|
letter = VfdChooseLetter();
|
|
}
|
|
|
|
if (letter) {
|
|
if (global) {
|
|
ret = VfdSetGlobalLink(hDevice, letter);
|
|
}
|
|
else {
|
|
ret = VfdSetLocalLink(hDevice, letter);
|
|
}
|
|
|
|
if (ret != ERROR_SUCCESS) {
|
|
PrintMessage(MSG_LINK_NG, number, letter);
|
|
printf("%s", SystemError(ret));
|
|
}
|
|
}
|
|
else {
|
|
PrintMessage(MSG_LINK_FULL);
|
|
}
|
|
|
|
PrintDriveLetter(hDevice, number);
|
|
|
|
CloseHandle(hDevice);
|
|
|
|
target_min++;
|
|
}
|
|
|
|
return VFD_OK;
|
|
}
|
|
|
|
//
|
|
// Remove a drive letter from a Virtual Floppy Drive
|
|
//
|
|
int Unlink(const char **args)
|
|
{
|
|
ULONG target_min = TARGET_NONE;
|
|
ULONG target_max = TARGET_NONE;
|
|
HANDLE hDevice;
|
|
DWORD ret;
|
|
|
|
while (args && *args) {
|
|
if ((isalnum(**args) || **args == '*') &&
|
|
(*(*args + 1) == ':' || *(*args + 1) == '\0')) {
|
|
|
|
if (target_min != TARGET_NONE) {
|
|
PrintMessage(MSG_DUPLICATE_ARGS, *args);
|
|
return VFD_NG;
|
|
}
|
|
|
|
if (**args == '*') {
|
|
target_min = '0';
|
|
target_max = '0' + VFD_MAXIMUM_DEVICES;
|
|
}
|
|
else {
|
|
target_min = **args;
|
|
target_max = target_min + 1;
|
|
}
|
|
}
|
|
else {
|
|
PrintMessage(MSG_UNKNOWN_OPTION, *args);
|
|
PrintMessage(MSG_HINT_ULINK, help_progname);
|
|
return VFD_NG;
|
|
}
|
|
|
|
args++;
|
|
}
|
|
|
|
if (target_min == TARGET_NONE) {
|
|
// default: drive 0
|
|
target_min = '0';
|
|
target_max = '1';
|
|
PrintMessage(MSG_TARGET_NOTICE, target_min);
|
|
}
|
|
|
|
// ensure that the driver is installed
|
|
|
|
if (driver_state == VFD_NOT_INSTALLED) {
|
|
PrintMessage(MSG_NOT_INSTALLED);
|
|
return VFD_NG;
|
|
}
|
|
|
|
// ensure that the driver is up to date
|
|
|
|
if (CheckDriver() != VFD_OK) {
|
|
return VFD_NG;
|
|
}
|
|
|
|
// ensure that the driver is running
|
|
|
|
if (driver_state != SERVICE_RUNNING) {
|
|
PrintMessage(MSG_NOT_STARTED);
|
|
return VFD_NG;
|
|
}
|
|
|
|
while (target_min < target_max) {
|
|
ULONG number;
|
|
|
|
hDevice = VfdOpenDevice(target_min);
|
|
|
|
if (hDevice == INVALID_HANDLE_VALUE) {
|
|
ret = GetLastError();
|
|
PrintMessage(MSG_ACCESS_NG, target_min);
|
|
printf("%s", SystemError(ret));
|
|
target_min++;
|
|
continue;
|
|
}
|
|
|
|
ret = VfdGetDeviceNumber(hDevice, &number);
|
|
|
|
if (ret != ERROR_SUCCESS) {
|
|
PrintMessage(MSG_ACCESS_NG, target_min);
|
|
printf("%s", SystemError(ret));
|
|
CloseHandle(hDevice);
|
|
target_min++;
|
|
continue;
|
|
}
|
|
|
|
VfdSetGlobalLink(hDevice, 0);
|
|
VfdSetLocalLink(hDevice, 0);
|
|
|
|
PrintDriveLetter(hDevice, number);
|
|
|
|
CloseHandle(hDevice);
|
|
|
|
target_min++;
|
|
}
|
|
|
|
return VFD_OK;
|
|
}
|
|
|
|
//
|
|
// Print current driver state
|
|
// Command Line Parameters: None
|
|
//
|
|
int Status(const char **args)
|
|
{
|
|
HANDLE hDevice;
|
|
TCHAR path[MAX_PATH];
|
|
DWORD start_type;
|
|
DWORD version;
|
|
ULONG target;
|
|
DWORD ret;
|
|
|
|
UNREFERENCED_PARAMETER(args);
|
|
|
|
if (driver_state == VFD_NOT_INSTALLED) {
|
|
PrintMessage(MSG_NOT_INSTALLED);
|
|
}
|
|
else {
|
|
|
|
// get current driver config
|
|
|
|
ret = VfdGetDriverConfig(path, &start_type);
|
|
|
|
if (ret != ERROR_SUCCESS) {
|
|
PrintMessage(MSG_GET_CONFIG_NG);
|
|
printf("%s", SystemError(ret));
|
|
return VFD_NG;
|
|
}
|
|
|
|
// print driver file path
|
|
|
|
PrintMessage(MSG_DRIVER_FILE, path);
|
|
|
|
// print driver version
|
|
version = 0;
|
|
|
|
if (driver_state == SERVICE_RUNNING) {
|
|
|
|
hDevice = VfdOpenDevice(0);
|
|
|
|
if (hDevice != INVALID_HANDLE_VALUE) {
|
|
ret = VfdGetDriverVersion(hDevice, &version);
|
|
|
|
CloseHandle(hDevice);
|
|
}
|
|
|
|
}
|
|
|
|
if (version == 0) {
|
|
ret = VfdCheckDriverFile(path, &version);
|
|
}
|
|
|
|
if (ret == ERROR_SUCCESS) {
|
|
PrintMessage(MSG_DRIVER_VERSION,
|
|
HIWORD(version) & 0x7fff,
|
|
LOWORD(version),
|
|
(version & 0x80000000) ? "(debug)" : "");
|
|
}
|
|
else {
|
|
PrintMessage(MSG_GET_VERSION_NG);
|
|
printf("%s", SystemError(ret));
|
|
}
|
|
|
|
|
|
// print driver start type
|
|
|
|
PrintMessage(MSG_START_TYPE);
|
|
|
|
switch (start_type) {
|
|
case SERVICE_AUTO_START:
|
|
PrintMessage(MSG_START_AUTO);
|
|
break;
|
|
|
|
case SERVICE_BOOT_START:
|
|
PrintMessage(MSG_START_BOOT);
|
|
break;
|
|
|
|
case SERVICE_DEMAND_START:
|
|
PrintMessage(MSG_START_DEMAND);
|
|
break;
|
|
|
|
case SERVICE_DISABLED:
|
|
PrintMessage(MSG_START_DISABLED);
|
|
break;
|
|
|
|
case SERVICE_SYSTEM_START :
|
|
PrintMessage(MSG_START_SYSTEM);
|
|
break;
|
|
|
|
default:
|
|
PrintMessage(MSG_UNKNOWN_LONG, start_type);
|
|
break;
|
|
}
|
|
|
|
// print current driver state
|
|
|
|
PrintMessage(MSG_DRIVER_STATUS);
|
|
|
|
switch (driver_state) {
|
|
case SERVICE_STOPPED:
|
|
PrintMessage(MSG_STATUS_STOPPED);
|
|
break;
|
|
|
|
case SERVICE_START_PENDING:
|
|
PrintMessage(MSG_STATUS_START_P);
|
|
break;
|
|
|
|
case SERVICE_STOP_PENDING:
|
|
PrintMessage(MSG_STATUS_STOP_P);
|
|
break;
|
|
|
|
case SERVICE_RUNNING:
|
|
PrintMessage(MSG_STATUS_RUNNING);
|
|
break;
|
|
|
|
case SERVICE_CONTINUE_PENDING:
|
|
PrintMessage(MSG_STATUS_CONT_P);
|
|
break;
|
|
|
|
case SERVICE_PAUSE_PENDING:
|
|
PrintMessage(MSG_STATUS_PAUSE_P);
|
|
break;
|
|
|
|
case SERVICE_PAUSED:
|
|
PrintMessage(MSG_STATUS_PAUSED);
|
|
break;
|
|
|
|
default:
|
|
PrintMessage(MSG_UNKNOWN_LONG, driver_state);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// print shell extension status
|
|
|
|
printf("\n");
|
|
|
|
if (VfdCheckHandlers() == ERROR_SUCCESS) {
|
|
PrintMessage(MSG_SHELLEXT_ENABLED);
|
|
}
|
|
else {
|
|
PrintMessage(MSG_SHELLEXT_DISABLED);
|
|
}
|
|
|
|
// if driver is not running, no more info
|
|
|
|
if (driver_state != SERVICE_RUNNING) {
|
|
return VFD_OK;
|
|
}
|
|
|
|
// print image information
|
|
|
|
for (target = 0; target < VFD_MAXIMUM_DEVICES; target++) {
|
|
HANDLE hDevice = VfdOpenDevice(target);
|
|
|
|
if (hDevice == INVALID_HANDLE_VALUE) {
|
|
ret = GetLastError();
|
|
PrintMessage(MSG_ACCESS_NG, target + '0');
|
|
printf("%s", SystemError(ret));
|
|
return VFD_NG;
|
|
}
|
|
|
|
PrintImageInfo(hDevice);
|
|
|
|
CloseHandle(hDevice);
|
|
}
|
|
|
|
return VFD_OK;
|
|
}
|
|
|
|
//
|
|
// Print usage help
|
|
//
|
|
int Help(const char **args)
|
|
{
|
|
DWORD msg = MSG_HELP_GENERAL;
|
|
char *buf = NULL;
|
|
|
|
if (args && *args) {
|
|
int cmd = ParseHelpTopic(*args);
|
|
|
|
if (cmd < 0) {
|
|
msg = MSG_HELP_HELP;
|
|
}
|
|
else {
|
|
msg = HelpMsg[cmd].help;
|
|
}
|
|
}
|
|
|
|
FormatMessage(
|
|
FORMAT_MESSAGE_FROM_HMODULE |
|
|
FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
|
FORMAT_MESSAGE_ARGUMENT_ARRAY,
|
|
NULL, msg, 0, (LPTSTR)&buf, 0,
|
|
(va_list *)&help_progname);
|
|
|
|
if (buf == NULL) {
|
|
printf("%s", SystemError(GetLastError()));
|
|
return VFD_NG;
|
|
}
|
|
|
|
ConsolePager(buf, TRUE);
|
|
LocalFree(buf);
|
|
|
|
return VFD_OK;
|
|
}
|
|
|
|
//
|
|
// Print version information
|
|
//
|
|
int Version(const char **args)
|
|
{
|
|
UNREFERENCED_PARAMETER(args);
|
|
|
|
printf(VFD_PRODUCT_DESC "\n" VFD_COPYRIGHT_STR "\n"
|
|
"http://chitchat.at.infoseek.co.jp/vmware/vfd.html\n");
|
|
|
|
return VFD_OK;
|
|
}
|
|
|
|
//
|
|
// Parse command parameter
|
|
//
|
|
int ParseCommand(const char *cmd)
|
|
{
|
|
#define CMD_MATCH_NONE -1
|
|
#define CMD_MATCH_MULTI -2
|
|
|
|
size_t len;
|
|
int idx;
|
|
int match;
|
|
|
|
// skip a leading '/'
|
|
|
|
if (*cmd == '/') {
|
|
cmd++;
|
|
}
|
|
|
|
if (*cmd == '\0') {
|
|
|
|
// empty command
|
|
|
|
return CMD_MATCH_NONE;
|
|
}
|
|
|
|
// find a match
|
|
len = strlen(cmd);
|
|
idx = 0;
|
|
match = CMD_MATCH_NONE;
|
|
|
|
while (Commands[idx].cmd) {
|
|
|
|
if (strlen(Commands[idx].cmd) >= len &&
|
|
!_strnicmp(cmd, Commands[idx].cmd, len)) {
|
|
|
|
if (match == CMD_MATCH_NONE) { // first match
|
|
match = idx;
|
|
}
|
|
else { // multiple matches
|
|
if (match != CMD_MATCH_MULTI) { // first time
|
|
PrintMessage(MSG_AMBIGUOUS_COMMAND, cmd);
|
|
printf("> %s ", Commands[match].cmd);
|
|
match = CMD_MATCH_MULTI;
|
|
}
|
|
|
|
printf("%s ", Commands[idx].cmd);
|
|
}
|
|
}
|
|
|
|
idx++;
|
|
}
|
|
|
|
if (match == CMD_MATCH_NONE) { // match not found
|
|
PrintMessage(MSG_UNKNOWN_COMMAND, cmd);
|
|
}
|
|
else if (match == CMD_MATCH_MULTI) { // multiple matches
|
|
printf("\n");
|
|
}
|
|
|
|
return match;
|
|
}
|
|
|
|
int ParseHelpTopic(const char *topic)
|
|
{
|
|
size_t len;
|
|
int idx;
|
|
int match;
|
|
|
|
if (*topic == '\0') {
|
|
|
|
// empty command
|
|
|
|
return CMD_MATCH_NONE;
|
|
}
|
|
|
|
// find a match
|
|
len = strlen(topic);
|
|
idx = 0;
|
|
match = CMD_MATCH_NONE;
|
|
|
|
while (HelpMsg[idx].keyword) {
|
|
|
|
if (strlen(HelpMsg[idx].keyword) >= len &&
|
|
!_strnicmp(topic, HelpMsg[idx].keyword, len)) {
|
|
|
|
if (match == CMD_MATCH_NONE) { // first match
|
|
match = idx;
|
|
}
|
|
else { // multiple matches
|
|
if (match != CMD_MATCH_MULTI) { // first time
|
|
PrintMessage(MSG_AMBIGUOUS_COMMAND, topic);
|
|
printf("> %s ", HelpMsg[match].keyword);
|
|
match = CMD_MATCH_MULTI;
|
|
}
|
|
|
|
printf("%s ", HelpMsg[idx].keyword);
|
|
}
|
|
}
|
|
|
|
idx++;
|
|
}
|
|
|
|
if (match == CMD_MATCH_NONE) { // match not found
|
|
PrintMessage(MSG_UNKNOWN_COMMAND, topic);
|
|
}
|
|
else if (match == CMD_MATCH_MULTI) { // multiple matches
|
|
printf("\n");
|
|
}
|
|
|
|
return match;
|
|
}
|
|
|
|
//
|
|
// Check driver version and update if necessary
|
|
//
|
|
int CheckDriver()
|
|
{
|
|
char path[MAX_PATH];
|
|
DWORD start;
|
|
|
|
// check installed driver file version
|
|
|
|
if (VfdGetDriverConfig(path, &start) == ERROR_SUCCESS &&
|
|
VfdCheckDriverFile(path, NULL) == ERROR_SUCCESS) {
|
|
|
|
HANDLE hDevice;
|
|
|
|
if (driver_state != SERVICE_RUNNING) {
|
|
return VFD_OK;
|
|
}
|
|
|
|
// check running driver version
|
|
|
|
hDevice = VfdOpenDevice(0);
|
|
|
|
if (hDevice != INVALID_HANDLE_VALUE) {
|
|
CloseHandle(hDevice);
|
|
return VFD_OK;
|
|
}
|
|
}
|
|
|
|
PrintMessage(MSG_WRONG_DRIVER);
|
|
return VFD_NG;
|
|
}
|
|
|
|
//
|
|
// Print a prompt message and accept the reply input
|
|
//
|
|
int InputChar(ULONG msg, PCSTR ans)
|
|
{
|
|
HANDLE hStdIn;
|
|
INPUT_RECORD input;
|
|
DWORD result;
|
|
int reply;
|
|
|
|
PrintMessage(msg);
|
|
fflush(NULL);
|
|
|
|
hStdIn = GetStdHandle(STD_INPUT_HANDLE);
|
|
|
|
FlushConsoleInputBuffer(hStdIn);
|
|
|
|
for (;;) {
|
|
ReadConsoleInput(hStdIn, &input, sizeof(input), &result);
|
|
|
|
if (input.EventType == KEY_EVENT &&
|
|
input.Event.KeyEvent.bKeyDown) {
|
|
|
|
reply = tolower(input.Event.KeyEvent.uChar.AsciiChar);
|
|
|
|
if (strchr(ans, reply)) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
printf("%c\n", reply);
|
|
|
|
return reply;
|
|
}
|
|
|
|
//
|
|
// Print image information on a Virtual Floppy Drive
|
|
//
|
|
void PrintImageInfo(
|
|
HANDLE hDevice)
|
|
{
|
|
ULONG device_number;
|
|
CHAR file_name[MAX_PATH];
|
|
CHAR file_desc[MAX_PATH];
|
|
VFD_DISKTYPE disk_type;
|
|
VFD_MEDIA media_type;
|
|
VFD_FLAGS media_flags;
|
|
VFD_FILETYPE file_type;
|
|
ULONG image_size;
|
|
DWORD ret;
|
|
|
|
printf("\n");
|
|
|
|
// get current device number
|
|
|
|
ret = VfdGetDeviceNumber(hDevice, &device_number);
|
|
|
|
if (ret != ERROR_SUCCESS) {
|
|
PrintMessage(MSG_GET_LINK_NG);
|
|
printf("%s", SystemError(ret));
|
|
device_number = (ULONG)-1;
|
|
}
|
|
|
|
// get current drive letters
|
|
|
|
PrintDriveLetter(hDevice, device_number);
|
|
|
|
// image file information
|
|
|
|
ret = VfdGetImageInfo(hDevice, file_name, &disk_type,
|
|
&media_type, &media_flags, &file_type, &image_size);
|
|
|
|
if (ret != ERROR_SUCCESS) {
|
|
PrintMessage(MSG_GET_FILE_NG);
|
|
printf("%s", SystemError(ret));
|
|
return;
|
|
}
|
|
|
|
// print image file information
|
|
if (media_type == VFD_MEDIA_NONE) {
|
|
PrintMessage(MSG_IMAGE_NONE);
|
|
return;
|
|
}
|
|
|
|
if (file_name[0]) {
|
|
PrintMessage(MSG_IMAGE_NAME, file_name);
|
|
|
|
VfdMakeFileDesc(file_desc, sizeof(file_desc),
|
|
file_type, image_size, GetFileAttributes(file_name));
|
|
}
|
|
else {
|
|
PrintMessage(MSG_IMAGE_NAME, "<RAM>");
|
|
|
|
VfdMakeFileDesc(file_desc, sizeof(file_desc),
|
|
VFD_FILETYPE_NONE, image_size, 0);
|
|
}
|
|
|
|
PrintMessage(MSG_FILE_DESC, file_desc);
|
|
|
|
if (disk_type == VFD_DISKTYPE_FILE) {
|
|
PrintMessage(MSG_DISKTYPE_FILE);
|
|
}
|
|
else {
|
|
if (media_flags & VFD_FLAG_DATA_MODIFIED) {
|
|
PrintMessage(MSG_DISKTYPE_RAM_DIRTY);
|
|
}
|
|
else {
|
|
PrintMessage(MSG_DISKTYPE_RAM_CLEAN);
|
|
}
|
|
}
|
|
|
|
// print other file info
|
|
|
|
PrintMessage(MSG_MEDIA_TYPE, VfdMediaTypeName(media_type));
|
|
|
|
if (media_flags & VFD_FLAG_WRITE_PROTECTED) {
|
|
PrintMessage(MSG_MEDIA_PROTECTED);
|
|
}
|
|
else {
|
|
PrintMessage(MSG_MEDIA_WRITABLE);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Print drive letters on a virtual floppy drive
|
|
//
|
|
void PrintDriveLetter(
|
|
HANDLE hDevice,
|
|
ULONG nDrive)
|
|
{
|
|
CHAR letter;
|
|
|
|
PrintMessage(MSG_DRIVE_LETTER, nDrive);
|
|
|
|
VfdGetGlobalLink(hDevice, &letter);
|
|
|
|
if (isalpha(letter)) {
|
|
PrintMessage(MSG_PERSISTENT, toupper(letter));
|
|
}
|
|
|
|
while (VfdGetLocalLink(hDevice, &letter) == ERROR_SUCCESS &&
|
|
isalpha(letter)) {
|
|
PrintMessage(MSG_EPHEMERAL, toupper(letter));
|
|
}
|
|
|
|
printf("\n");
|
|
}
|
|
|
|
//
|
|
// Prints a text on screen a page a time
|
|
//
|
|
BOOL ConsolePager(char *pBuffer, BOOL bReset)
|
|
{
|
|
static int rows = 0;
|
|
char prompt[80];
|
|
int prompt_len = 0;
|
|
HANDLE hStdOut;
|
|
HANDLE hStdIn;
|
|
|
|
//
|
|
// prepare the console input and output handles
|
|
//
|
|
hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
|
|
hStdIn = GetStdHandle(STD_INPUT_HANDLE);
|
|
|
|
for (;;) {
|
|
CONSOLE_SCREEN_BUFFER_INFO info;
|
|
INPUT_RECORD input;
|
|
DWORD result;
|
|
DWORD mode;
|
|
int cols;
|
|
char *cur;
|
|
char save;
|
|
|
|
//
|
|
// Get the current console screen information
|
|
//
|
|
GetConsoleScreenBufferInfo(hStdOut, &info);
|
|
|
|
if (bReset || rows <= 0) {
|
|
rows = info.srWindow.Bottom - info.srWindow.Top - 1;
|
|
}
|
|
|
|
cols = info.dwSize.X;
|
|
|
|
// console window is too small for paging
|
|
|
|
if (rows <= 0) {
|
|
// print all text and exit
|
|
printf("%s", pBuffer);
|
|
break;
|
|
}
|
|
|
|
//
|
|
// find the tail of the text to be printed this time
|
|
//
|
|
cur = pBuffer;
|
|
save = '\0';
|
|
|
|
while (*cur) {
|
|
if (*(cur++) == '\n' || (cols--) == 0) {
|
|
// reached the end of a line
|
|
if (--rows == 0) {
|
|
// reached the end of a page
|
|
// insert a terminating NULL char
|
|
save = *cur;
|
|
*cur = '\0';
|
|
break;
|
|
}
|
|
|
|
cols = info.dwSize.X;
|
|
}
|
|
}
|
|
|
|
// print the current page
|
|
printf("%s", pBuffer);
|
|
|
|
// end of the whole text?
|
|
if (save == '\0') {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// prompt for the next page
|
|
//
|
|
|
|
// prepare the prompt text
|
|
|
|
if (prompt_len == 0) {
|
|
|
|
prompt_len = FormatMessage(
|
|
FORMAT_MESSAGE_FROM_HMODULE |
|
|
FORMAT_MESSAGE_IGNORE_INSERTS,
|
|
NULL, MSG_PAGER_PROMPT, 0,
|
|
prompt, sizeof(prompt), NULL);
|
|
|
|
if (prompt_len == 0) {
|
|
strcpy(prompt, "Press any key to continue...");
|
|
prompt_len = strlen(prompt);
|
|
}
|
|
}
|
|
|
|
// get the current console input mode
|
|
|
|
GetConsoleMode(hStdIn, &mode);
|
|
|
|
// change the mode to receive Ctrl+C as a regular input
|
|
|
|
SetConsoleMode(hStdIn, (mode & ~ENABLE_PROCESSED_INPUT));
|
|
|
|
// get the current cursor position
|
|
|
|
GetConsoleScreenBufferInfo(hStdOut, &info);
|
|
|
|
// print the prompt text
|
|
|
|
WriteConsoleOutputCharacter(hStdOut, prompt,
|
|
prompt_len, info.dwCursorPosition, &result);
|
|
|
|
// reverse the text color
|
|
|
|
FillConsoleOutputAttribute(hStdOut,
|
|
(WORD)(info.wAttributes | COMMON_LVB_REVERSE_VIDEO),
|
|
prompt_len, info.dwCursorPosition, &result);
|
|
|
|
// move cursor to the end of the prompt text
|
|
|
|
info.dwCursorPosition.X =
|
|
(short)(info.dwCursorPosition.X + prompt_len);
|
|
|
|
SetConsoleCursorPosition(hStdOut, info.dwCursorPosition);
|
|
|
|
// wait for a key press event
|
|
|
|
FlushConsoleInputBuffer(hStdIn);
|
|
|
|
do {
|
|
ReadConsoleInput(hStdIn, &input, sizeof(input), &result);
|
|
}
|
|
while (input.EventType != KEY_EVENT ||
|
|
!input.Event.KeyEvent.bKeyDown ||
|
|
!input.Event.KeyEvent.uChar.AsciiChar);
|
|
|
|
// restore the original cursor position
|
|
|
|
info.dwCursorPosition.X =
|
|
(short)(info.dwCursorPosition.X - prompt_len);
|
|
|
|
SetConsoleCursorPosition(hStdOut, info.dwCursorPosition);
|
|
|
|
// delete the prompt text
|
|
|
|
FillConsoleOutputCharacter(hStdOut, ' ',
|
|
prompt_len, info.dwCursorPosition, &result);
|
|
|
|
// restore the text attribute to norml
|
|
|
|
FillConsoleOutputAttribute(hStdOut, info.wAttributes,
|
|
prompt_len, info.dwCursorPosition, &result);
|
|
|
|
// restore the original console mode
|
|
|
|
SetConsoleMode(hStdIn, mode);
|
|
|
|
// check if the input was 'q', <esc> or <Ctrl+C> ?
|
|
|
|
if (input.Event.KeyEvent.uChar.AsciiChar == VK_CANCEL ||
|
|
input.Event.KeyEvent.uChar.AsciiChar == VK_ESCAPE ||
|
|
tolower(input.Event.KeyEvent.uChar.AsciiChar) == 'q') {
|
|
|
|
// cancelled by the user
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// process the next page
|
|
//
|
|
*cur = save;
|
|
pBuffer = cur;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Format and print a message text
|
|
//
|
|
void PrintMessage(UINT msg, ...)
|
|
{
|
|
char *buf = NULL;
|
|
va_list list;
|
|
|
|
va_start(list, msg);
|
|
|
|
if (FormatMessage(
|
|
FORMAT_MESSAGE_FROM_HMODULE |
|
|
FORMAT_MESSAGE_ALLOCATE_BUFFER,
|
|
NULL, msg, 0, (LPTSTR)&buf, 0, &list)) {
|
|
|
|
printf("%s", buf);
|
|
}
|
|
else {
|
|
printf("Unknown Message ID %u\n", msg);
|
|
}
|
|
|
|
va_end(list);
|
|
|
|
if (buf) {
|
|
LocalFree(buf);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Return a system error message text
|
|
//
|
|
const char *SystemError(DWORD err)
|
|
{
|
|
static char msg[256];
|
|
|
|
if (!FormatMessage(
|
|
FORMAT_MESSAGE_FROM_SYSTEM |
|
|
FORMAT_MESSAGE_IGNORE_INSERTS,
|
|
NULL, err, 0, msg, sizeof(msg), NULL)) {
|
|
#ifndef __REACTOS__
|
|
sprintf(msg, "Unknown system error %lu (0x%08x)\n", err, err);
|
|
#else
|
|
sprintf(msg, "Unknown system error %lu (0x%08lx)\n", err, err);
|
|
#endif
|
|
}
|
|
|
|
return msg;
|
|
}
|
|
|
|
//
|
|
// Convert a path to match the case of names on the disk
|
|
//
|
|
void ConvertPathCase(char *src, char *dst)
|
|
{
|
|
HANDLE hFind;
|
|
WIN32_FIND_DATA find;
|
|
char *p;
|
|
|
|
p = dst;
|
|
|
|
if (*src == '\"') {
|
|
src++;
|
|
}
|
|
|
|
if (*(src + strlen(src) - 1) == '\"') {
|
|
*(src + strlen(src) - 1) = '\0';
|
|
}
|
|
|
|
//
|
|
// handle drive / remote server name
|
|
//
|
|
if (isalpha(*src) && *(src + 1) == ':') {
|
|
|
|
// drive name
|
|
|
|
*(p++) = (char)toupper(*src);
|
|
strcpy(p++, ":\\");
|
|
|
|
src += 2;
|
|
}
|
|
else if (*src == '\\' || *src == '/') {
|
|
|
|
// absolute path or remote name
|
|
|
|
if ((*(src + 1) == '\\' || *(src + 1) == '/') &&
|
|
*(src + 2) && *(src + 2) != '\\' && *(src + 2) != '/') {
|
|
|
|
// remote path
|
|
|
|
*(p++) = '\\';
|
|
*(p++) = '\\';
|
|
src += 2;
|
|
|
|
while (*src && *src != '\\' && *src != '/') {
|
|
*(p++) = *(src++);
|
|
}
|
|
}
|
|
|
|
strcpy(p, "\\");
|
|
}
|
|
else {
|
|
*p = '\0';
|
|
}
|
|
|
|
// skip redundant '\'
|
|
|
|
while (*src == '\\' || *src == '/') {
|
|
src++;
|
|
}
|
|
|
|
// process the path
|
|
|
|
while (*src) {
|
|
|
|
char *q = src;
|
|
|
|
// separate the next part
|
|
|
|
while (*q && *q != '\\' && *q != '/') {
|
|
q++;
|
|
}
|
|
|
|
if ((q - src) == 2 && !strncmp(src, "..", 2)) {
|
|
// parent dir - copy as it is
|
|
if (p != dst) {
|
|
*p++ = '\\';
|
|
}
|
|
|
|
strcpy(p, "..");
|
|
p += 2;
|
|
}
|
|
else if ((q - src) > 1 || *src != '.') {
|
|
// path name other than "."
|
|
if (p != dst) {
|
|
*(p++) = '\\';
|
|
}
|
|
|
|
strncpy(p, src, (q - src));
|
|
*(p + (q - src)) = '\0';
|
|
|
|
hFind = FindFirstFile(dst, &find);
|
|
|
|
if (hFind == INVALID_HANDLE_VALUE) {
|
|
strcpy(p, src);
|
|
break;
|
|
}
|
|
|
|
FindClose(hFind);
|
|
|
|
strcpy(p, find.cFileName);
|
|
p += strlen(p);
|
|
}
|
|
|
|
// skip trailing '\'s
|
|
|
|
while (*q == '\\' || *q == '/') {
|
|
q++;
|
|
}
|
|
|
|
src = q;
|
|
}
|
|
}
|