From 8a3642bf9a420be8df8bb0fb9d1c55e9dbe21785 Mon Sep 17 00:00:00 2001 From: Daniel Markstedt Date: Tue, 14 Sep 2021 19:51:12 -0700 Subject: [PATCH] Move to protobuf for the webapp, major overhaul to easyinstall.sh, code comment translations (#229) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Making saving and loading config files work with protobuf * Formatted the Status column, and fixed the available ID logic * Updated handling of removed status for devices without image file support * Comment update * Fixed typo * Updated logging * Updated handling of removed status for devices without image file support * Comment update * Fixed typo * Updated logging * Better handling of device status * Updated parameter handling * Updated setting default interfaces * Revert "Updated setting default interfaces" This reverts commit 210abc775d9a79dd0c631cf3877966a2923f4d5b. * Revert "Updated parameter handling" This reverts commit 35302addd59f5f5e1cc032888ba32dcbb426a846. * Abort with a 404 if rascsi is not running. Use any protobuf response to determine whether rascsi is running (should hardly be required anymore due to the other change, but just in case). * Move id reservation back into __main__ * Remove check for device type when validating Removed image * Leverage device property data for better status messages * Remove redundant string sanitation when reading config csv file * Clean up device list generation * Cleanup * Remove duplicates before building valid scsi id list * Fully translated cfilesystem.h code comments to English; partially translated cfilesystem.cpp * rascsi supports reserving IDs * Updated help message * Replaced BOOL by bool * Logging update * Logging update * Cleanup * Restructure the easyinstall.sh script to combine the install/update flows, and disallow installing the webapp by itself * Remove redundant steps handled in Makefile * Add the functionality to specify connect_type through a parameter * Add validation to the argument parser allowing only STANDARD and FULLSPEC as options * Complete translation of code comments for cfilesystem.h; partial translation for cfilesystem.cpp * Cleanup * Merge parts of the Network Assistant script by sonique6784; fix the run_choice startup parameter * Improve on the network setup messages * Fix routing address * Add checks for previous configuration; cleanup * Cleanup * Remove redundant step in wired setup. Improve messages. * Cleanup * Added default parameters to device properties * Return parameters a device was set up with * Add flows for configuring custom network settings; adopting some logic by –sonique6784 * Improved device initialization * Updated default parameter handling * Updated default parameter handling * Fixed typo * Comment updates * Comment update * Make iso generation work again, and add error handling to urllib actions * Manage default parameters in the respective device * Print available network interfaces. Clean up step and improve descriptive messages. * Make the script clean up previous configurations * Make the script only show relevant interfaces * Partial translation of cfilesystem.cpp * Do not pass empty parameter string * Added supports_params flag * Completely translate code comments in cfilesystem.cpp * Show rascsi-web status after installing * Refactoring * Made comparisons more consistent * Updated error handling * Updated exception handling * Made comparisons more consistent * Updated error handling * Overlooked code comment translation * Renaming * Better error handling for socket connection * Disable two NEC hd image types due to issue#232 * Comment update * NEC sectors size must be 512 bytes * Updated logging * Updated vendor name handling * Updated handling of media loading/unloading * Comment update * NEC sectors size must be 512 bytes * Updated logging * Updated vendor name handling * Updated handling of media loading/unloading * Better handling of removable disks in the web ui * Added stoppable property and stopped status * Made MO stoppable * Removed duplicate code * Removed duplicate code * Copy read-only property * Renaming * Add an assistant for reserving scsi ids * Don't show action if no device attached * Implement a device_info app path, and cut down on device columns always shown * Cleanup * Removed duplicate code, added START/STOP * Improved default parameter handling * Updated load/eject handling * Logging update * Fixed typo * Verified START/STOP UNIT * Updated logging * Updated status handling * Updated status handling * More status handling updates * Logging update * Made instance fields local variables * Removed duplicate code, added START/STOP * Improved default parameter handling * Updated load/eject handling * Logging update * Fixed typo * Verified START/STOP UNIT * Updated logging * Updated status handling * Updated status handling * More status handling updates * Logging update * Made instance fields local variables * Made disk_t private * Made some data structures private * Fixed ARM compile issue * Fast forward instead of rebase existing git repo * Fixed ctapdriver initialization issue * Reset read-only status when opening an image file * Cleanup * Cleanup * Made logging more consistent * Updated log level * Cleanup * Log load/eject on error level for testing * Revert "Log load/eject on error level for testing" This reverts commit d35a15ea8e520517d25e1e1054ad1aeda9f85f2e. * Assume drive is not ready after having been stopped * Updated status handling * Make the csv config files store all relevant device data for reading * Read 9 column csv config files * Fixed typo * Rebuild manpage * Fixed issue #234 (MODE SENSE (10) returns wrong mode parameter header) * Removed unused code * Enum data type update * Removed duplicate range check * Removed duplicate code * Removed more duplicate code * Logging update * SCCD sector size was not meant to be configurable * Better error handling for csv reading and writing * Updated configurable sector size properties * Removed assertion * Improved error handling * Updated error handling * Re-added special error handling only relevant for SASI * Added TODOs * Comment update * Added override modifier * Removed obsolete debug flag (related code was not called) * Comment and logging updates * Removed obsolete try/catch * Revert "Removed obsolete try/catch" This reverts commit 39ca12d8b153c706316ce79f4fec65c9abc60024. * Comment update * Removed duplicate code * Updated error messages, use more foreach loops * Avoid storing RaSCSI generated product info in config file * Updated logging * Logging update * Save config files in json instead of csv * Fix bugs with json config loading * Refactoring & remove unused code * Refactoring * Display upper case file endings in file management list * Only show product vendor for non-RaSCSI devices in the device list * Translate code comment * Refactoring * Fix bad identation * Improve valid file extension handling * Add validation when attaching removable media * Display valid file endings under the file list * Cleanup * Don't store 0 block size * Fix indentation * Read and write config files in key:pair format * Add section for controlling logging * README update * Added block_count * Cleanup, fix typos * Support attaching CD-ROM with custom block size * Evaluate block size when inserting a media * rasctl display capacity if available * Info message update * Use kwargs for device attachment * Fix bugs in attach_image kwargs; make config file more readable * POC for attaching device with profile * Only list product types valid for the particular image file * Perform validation of HDD image size based on the product profile * Implement sidecar config files for drive images. * Added missing product name to NEC vital product data * MO block size depends on capacity only * Better error handling for device sidecar config loading * Extended property/status display * Property display update * Updated error handling * Handle image sizes in bytes internally * Revert change * Resolve bad merge Co-authored-by: Uwe Seimet --- .gitignore | 1 + easyinstall.sh | 396 +++++-- src/raspberrypi/devices/cfilesystem.cpp | 1426 +++++++++++------------ src/raspberrypi/devices/cfilesystem.h | 714 ++++++------ src/raspberrypi/fileio.cpp | 2 +- src/web/file_cmds.py | 161 ++- src/web/pi_cmds.py | 9 + src/web/ractl_cmds.py | 408 +++++-- src/web/requirements.txt | 1 + src/web/settings.py | 16 +- src/web/static/style.css | 7 +- src/web/templates/index.html | 122 +- src/web/web.py | 273 +++-- 13 files changed, 1999 insertions(+), 1537 deletions(-) diff --git a/.gitignore b/.gitignore index 69e06bf4..3e78e42d 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ core *.swp __pycache__ src/web/current +src/web/rascsi_interface_pb2.py src/oled_monitor/current src/raspberrypi/hfdisk/ *~ diff --git a/easyinstall.sh b/easyinstall.sh index d9cc1505..012d7bf6 100755 --- a/easyinstall.sh +++ b/easyinstall.sh @@ -20,6 +20,32 @@ logo=""" echo -e $logo } +function showMacNetworkWired(){ +logo=""" +                              .-~-.-~~~-.~-.\n + ╔═══════╗                  .(              )\n + ║|¯¯¯¯¯|║                 /               \`.\n + ║|_____|║>--------------<~               .   )\n + ║ .  __ ║                 (              :'-'\n + ╚╦═════╦╝                  ~-.________.:'\n +  ¯¯¯¯¯¯¯\n +""" +echo -e $logo +} + +function showMacNetworkWireless(){ +logo=""" +                              .-~-.-~~~-.~-.\n + ╔═══════╗        .(       .(              )\n + ║|¯¯¯¯¯|║  .(  .(        /               \`.\n + ║|_____|║ .o    o       ~               .   )\n + ║ .  __ ║  '(  '(        (              :'-'\n + ╚╦═════╦╝        '(       ~-.________.:'\n +  ¯¯¯¯¯¯¯\n +""" +echo -e $logo +} + VIRTUAL_DRIVER_PATH=/home/pi/images HFS_FORMAT=/usr/bin/hformat HFDISK_BIN=/usr/bin/hfdisk @@ -41,18 +67,19 @@ function initialChecks() { fi } +# install all dependency packages for RaSCSI Service function installPackages() { sudo apt-get update && sudo apt install git libspdlog-dev libpcap-dev genisoimage python3 python3-venv nginx libpcap-dev protobuf-compiler bridge-utils python3-dev libev-dev libevdev2 -y } -# install all dependency packages for RaSCSI Service # compile and install RaSCSI Service function installRaScsi() { - installPackages + sudo systemctl stop rascsi cd ~/RASCSI/src/raspberrypi - make all CONNECT_TYPE=FULLSPEC - sudo make install CONNECT_TYPE=FULLSPEC + make clean + make all CONNECT_TYPE=${CONNECT_TYPE-FULLSPEC} + sudo make install CONNECT_TYPE=${CONNECT_TYPE-FULLSPEC} sudoIsReady=$(sudo grep -c "rascsi" /etc/sudoers) @@ -71,7 +98,39 @@ www-data ALL=NOPASSWD: /sbin/shutdown, /sbin/reboot sudo systemctl start rascsi } +# install everything required to run an HTTP server (Nginx + Python Flask App) +function installRaScsiWebInterface() { + echo "Compiling the Python protobuf library..." + [ -f ~/RASCSI/src/web/rascsi_interface.proto ] && rm ~/RASCSI/src/web/rascsi_interface.proto + protoc -I=/home/pi/RASCSI/src/raspberrypi/ --python_out=/home/pi/RASCSI/src/web/ rascsi_interface.proto + + sudo cp -f ~/RASCSI/src/web/service-infra/nginx-default.conf /etc/nginx/sites-available/default + sudo cp -f ~/RASCSI/src/web/service-infra/502.html /var/www/html/502.html + + sudo usermod -a -G pi www-data + + sudo systemctl reload nginx + + echo "Installing the rascsi-web.service configuration..." + sudo cp ~/RASCSI/src/web/service-infra/rascsi-web.service /etc/systemd/system/rascsi-web.service + + sudo systemctl daemon-reload + sudo systemctl enable rascsi-web + sudo systemctl start rascsi-web +} + +function createImagesDir() { + if [ -d $VIRTUAL_DRIVER_PATH ]; then + echo "The $VIRTUAL_DRIVER_PATH directory already exists." + else + echo "The $VIRTUAL_DRIVER_PATH directory does not exist; creating..." + mkdir -p $VIRTUAL_DRIVER_PATH + chmod -R 775 $VIRTUAL_DRIVER_PATH + fi +} + function stopOldWebInterface() { + sudo systemctl stop rascsi-web APACHE_STATUS=$(sudo systemctl status apache2 &> /dev/null; echo $?) if [ "$APACHE_STATUS" -eq 0 ] ; then echo "Stopping old Apache2 RaSCSI Web..." @@ -80,28 +139,6 @@ function stopOldWebInterface() { fi } -# install everything required to run an HTTP server (Nginx + Python Flask App) -function installRaScsiWebInterface() { - stopOldWebInterface - installPackages - - sudo cp -f ~/RASCSI/src/web/service-infra/nginx-default.conf /etc/nginx/sites-available/default - sudo cp -f ~/RASCSI/src/web/service-infra/502.html /var/www/html/502.html - - mkdir -p $VIRTUAL_DRIVER_PATH - chmod -R 775 $VIRTUAL_DRIVER_PATH - groups www-data - sudo usermod -a -G pi www-data - groups www-data - - sudo systemctl reload nginx - - sudo cp ~/RASCSI/src/web/service-infra/rascsi-web.service /etc/systemd/system/rascsi-web.service - sudo systemctl daemon-reload - sudo systemctl enable rascsi-web - sudo systemctl start rascsi-web -} - function updateRaScsiGit() { echo "Updating checked out branch $GIT_REMOTE/$GIT_BRANCH" cd ~/RASCSI @@ -112,8 +149,7 @@ function updateRaScsiGit() { stashed=1 fi - git fetch $GIT_REMOTE - git rebase $GIT_REMOTE/$GIT_BRANCH + git pull --ff-only if [ $stashed -eq 1 ]; then echo "Reapplying local changes..." @@ -121,33 +157,14 @@ function updateRaScsiGit() { fi } -function updateRaScsi() { - updateRaScsiGit - installPackages - sudo systemctl stop rascsi - - cd ~/RASCSI/src/raspberrypi - - make clean - make all CONNECT_TYPE=FULLSPEC - sudo make install CONNECT_TYPE=FULLSPEC - sudo systemctl start rascsi -} - -function updateRaScsiWebInterface() { - stopOldWebInterface - updateRaScsiGit - sudo cp -f ~/RASCSI/src/web/service-infra/nginx-default.conf /etc/nginx/sites-available/default - sudo cp -f ~/RASCSI/src/web/service-infra/502.html /var/www/html/502.html - echo "Restarting rascsi-web services..." - sudo systemctl restart rascsi-web - sudo systemctl restart nginx -} - function showRaScsiStatus() { sudo systemctl status rascsi | tee } +function showRaScsiWebStatus() { + sudo systemctl status rascsi-web | tee +} + function createDrive600MB() { createDrive 600 "HD600" } @@ -184,6 +201,22 @@ function formatDrive() { fi # Inject hfdisk commands to create Drive with correct partitions + # https://www.codesrc.com/mediawiki/index.php/HFSFromScratch + # i initialize partition map + # continue with default first block + # C Create 1st partition with type specified next) + # continue with default + # 32 32 blocks (required for HFS+) + # Driver_Partition Partition Name + # Apple_Driver Partition Type (available types: Apple_Driver, Apple_Driver43, Apple_Free, Apple_HFS...) + # C Create 2nd partition with type specified next + # continue with default first block + # continue with default block size (rest of the disk) + # ${volumeName} Partition name provided by user + # Apple_HFS Partition Type + # w Write partition map to disk + # y Confirm partition table + # p Print partition map (echo i; echo ; echo C; echo ; echo 32; echo "Driver_Partition"; echo "Apple_Driver"; echo C; echo ; echo ; echo "${volumeName}"; echo "Apple_HFS"; echo w; echo y; echo p;) | $HFDISK_BIN "$diskPath" partitionOk=$? @@ -242,55 +275,208 @@ function createDrive() { fi } +function setupWiredNetworking() { + echo "Setting up wired network..." + + LAN_INTERFACE=eth0 + + echo "$LAN_INTERFACE will be configured for network forwarding with DHCP." + echo "" + echo "WARNING: If you continue, the IP address of your Pi may change upon reboot." + echo "Please make sure you will not lose access to the Pi system." + echo "" + echo "Do you want to proceed with network configuration using the default settings? Y/n" + read REPLY + + if [ "$REPLY" == "N" ] || [ "$REPLY" == "n" ]; then + echo "Available wired interfaces on this system:" + ip -o addr show scope link | awk '{split($0, a); print $2}' | grep eth + echo "Please type the wired interface you want to use and press Enter:" + read -r SELECTED + LAN_INTERFACE=$SELECTED + fi + + if [ $(grep -c "^denyinterfaces" /etc/dhcpcd.conf) -ge 1 ]; then + echo "WARNING: Network forwarding may already have been configured. Proceeding will overwrite the configuration." + echo "Press enter to continue or CTRL-C to exit" + read REPLY + sudo sed -i /^denyinterfaces/d /etc/dhcpcd.conf + fi + sudo echo "denyinterfaces $LAN_INTERFACE" >> /etc/dhcpcd.conf + echo "Modified /etc/dhcpcd.conf" + + # default config file is made for eth0, this will set the right net interface + sudo bash -c 'sed s/eth0/'"$LAN_INTERFACE"'/g /home/pi/RASCSI/src/raspberrypi/os_integration/rascsi_bridge > /etc/network/interfaces.d/rascsi_bridge' + echo "Modified /etc/network/interfaces.d/rascsi_bridge" + + echo "Configuration completed!" + echo "Please make sure you attach ia DaynaPORT network adapter to the RaSCSI configuration." + echo "Either use the Web UI, or do this on the command line (assuming SCSI ID 6): \"rascsi -ID 6 -t scdp $LAN_INTERFACE\"" + echo "" + echo "We need to reboot your Pi" + echo "Press Enter to reboot or CTRL-C to exit" + read + + echo "Rebooting..." + sleep 3 + sudo reboot +} + +function setupWirelessNetworking() { + NETWORK="10.10.20" + IP=$NETWORK.2 # Macintosh or Device IP + NETWORK_MASK="255.255.255.0" + CIDR="24" + ROUTER_IP=$NETWORK.1 + ROUTING_ADDRESS=$NETWORK.0/$CIDR + WLAN_INTERFACE="wlan0" + + echo "$WLAN_INTERFACE will be configured for network forwarding with static IP assignment." + echo "Configure your Macintosh or other device with the following:" + echo "IP Address (static): $IP" + echo "Router Address: $ROUTER_IP" + echo "Subnet Mask: $NETWORK_MASK" + echo "DNS Server: Any public DNS server" + echo "" + echo "Do you want to proceed with network configuration using the default settings? Y/n" + read REPLY + + if [ "$REPLY" == "N" ] || [ "$REPLY" == "n" ]; then + echo "Available wireless interfaces on this system:" + ip -o addr show scope link | awk '{split($0, a); print $2}' | grep wlan + echo "Please type the wireless interface you want to use and press Enter:" + read -r WLAN_INTERFACE + echo "Base IP address (ex. 10.10.20):" + read -r NETWORK + echo "CIDR for Subnet Mask (ex. '24' for 255.255.255.0):" + read -r CIDR + ROUTER_IP=$NETWORK.1 + ROUTING_ADDRESS=$NETWORK.0/$CIDR + fi + + + if [ $(grep -c "^net.ipv4.ip_forward=1" /etc/sysctl.conf) -ge 1 ]; then + echo "WARNING: Network forwarding may already have been configured. Proceeding will overwrite the configuration." + echo "Press enter to continue or CTRL-C to exit" + read REPLY + else + sudo bash -c 'echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf' + echo "Modified /etc/sysctl.conf" + fi + + sudo iptables --flush + sudo iptables -t nat -F + sudo iptables -X + sudo iptables -Z + sudo iptables -P INPUT ACCEPT + sudo iptables -P OUTPUT ACCEPT + sudo iptables -P FORWARD ACCEPT + sudo iptables -t nat -A POSTROUTING -o $WLAN_INTERFACE -s $ROUTING_ADDRESS -j MASQUERADE + + # Check if iptables-persistent is installed + IPTABLES_PERSISTENT=$(dpkg -s iptables-persistent | grep Status | grep -c "install ok") + if [ $IPTABLES_PERSISTENT -eq 0 ]; then + sudo apt-get install iptables-persistent --assume-yes + else + sudo iptables-save --file /etc/iptables/rules.v4 + fi + echo "Modified /etc/iptables/rules.v4" + + echo "Configuration completed!" + echo "" + echo "Please make sure you attach a DaynaPORT network adapter to the RaSCSI configuration" + echo "Either use the Web UI, or do this on the command line (assuming SCSI ID 6): \"rascsi -ID 6 -t scdp $WLAN_INTERFACE:$ROUTER_IP/$CIDR\"" + echo "" + echo "We need to reboot your Pi" + echo "Press Enter to reboot or CTRL-C to exit" + read REPLY + + echo "Rebooting..." + sleep 3 + sudo reboot +} + +function reserveScsiIds() { + if [ ! -f /etc/systemd/system/rascsi-web.service ]; then + echo "This feature depends on the RaSCSI Web UI being installed. Please install RaSCSI Web before continuing." + exit + fi + + sudo systemctl stop rascsi-web + echo "Please type the SCSI ID(s) that you want to reserve and press Enter:" + echo "The input should be a string of digits without separators, e.g. \"017\" for IDs 0, 1, and 7." + read -r RESERVED_IDS + sudo sed -i /^ExecStart=/d /etc/systemd/system/rascsi-web.service + sudo sed -i "8 i ExecStart=/home/pi/RASCSI/src/web/start.sh --reserved_ids=$RESERVED_IDS" /etc/systemd/system/rascsi-web.service + + sudo systemctl daemon-reload + sudo systemctl start rascsi-web +} + function runChoice() { case $1 in 0) - echo "Installing RaSCSI Service + Web interface" + echo "Installing / Updating RaSCSI Service (${CONNECT_TYPE-FULLSPEC}) + Web interface + 600MB Drive" + stopOldWebInterface + updateRaScsiGit + createImagesDir + installPackages installRaScsi installRaScsiWebInterface createDrive600MB showRaScsiStatus - echo "Installing RaSCSI Service + Web interface - Complete!" + showRaScsiWebStatus + echo "Installing / Updating RaSCSI Service (${CONNECT_TYPE-FULLSPEC}) + Web interface + 600MB Drive - Complete!" ;; 1) - echo "Installing RaSCSI Service" + echo "Installing / Updating RaSCSI Service (${CONNECT_TYPE-FULLSPEC}) + Web interface" + stopOldWebInterface + updateRaScsiGit + createImagesDir + installPackages installRaScsi + installRaScsiWebInterface showRaScsiStatus - echo "Installing RaSCSI Service - Complete!" + showRaScsiWebStatus + echo "Installing / Updating RaSCSI Service (${CONNECT_TYPE-FULLSPEC}) + Web interface - Complete!" ;; 2) - echo "Installing RaSCSI Web interface" - installRaScsiWebInterface - echo "Installing RaSCSI Web interface - Complete!" - ;; + echo "Installing / Updating RaSCSI Service (${CONNECT_TYPE-FULLSPEC})" + updateRaScsiGit + createImagesDir + installPackages + installRaScsi + showRaScsiStatus + echo "Installing / Updating RaSCSI Service (${CONNECT_TYPE-FULLSPEC}) - Complete!" + ;; 3) - echo "Updating RaSCSI Service + Web interface" - updateRaScsi - updateRaScsiWebInterface - showRaScsiStatus - echo "Updating RaSCSI Service + Web interface - Complete!" - ;; - 4) - echo "Updating RaSCSI Service" - updateRaScsi - showRaScsiStatus - echo "Updating RaSCSI Service - Complete!" - ;; - 5) - echo "Updating RaSCSI Web interface" - updateRaScsiWebInterface - echo "Updating RaSCSI Web interface - Complete!" - ;; - 6) echo "Creating a 600MB drive" createDrive600MB echo "Creating a 600MB drive - Complete!" ;; - 7) + 4) echo "Creating a custom drive" createDriveCustom echo "Creating a custom drive - Complete!" ;; + 5) + echo "Configuring wired network bridge" + showMacNetworkWired + setupWiredNetworking + echo "Configuring wired network bridge - Complete!" + ;; + 6) + echo "Configuring wifi network bridge" + showMacNetworkWireless + setupWirelessNetworking + echo "Configuring wifi network bridge - Complete!" + ;; + 7) + echo "Reserving SCSI IDs" + reserveScsiIds + showRaScsiWebStatus + echo "Reserving SCSI IDs - Complete!" + ;; -h|--help|h|help) showMenu ;; @@ -314,25 +500,53 @@ function readChoice() { function showMenu() { echo "" echo "Choose among the following options:" - echo "INSTALL" - echo " 0) install RaSCSI Service + web interface + 600MB Drive (recommended)" - echo " 1) install RaSCSI Service (initial)" - echo " 2) install RaSCSI Web interface" - echo "UPDATE" - echo " 3) update RaSCSI Service + web interface (recommended)" - echo " 4) update RaSCSI Service" - echo " 5) update RaSCSI Web interface" - echo "CREATE EMPTY DRIVE" - echo " 6) 600MB drive (recommended)" - echo " 7) custom drive size (up to 4000MB)" + echo "INSTALL/UPDATE RASCSI (${CONNECT_TYPE-FULLSPEC} version)" + echo " 0) install or update RaSCSI Service + web interface + 600MB Drive (recommended)" + echo " 1) install or update RaSCSI Service + web interface" + echo " 2) install or update RaSCSI Service" + echo "CREATE EMPTY DRIVE IMAGE" + echo " 3) 600MB drive (recommended)" + echo " 4) custom drive size (up to 4000MB)" + echo "NETWORK ASSISTANT" + echo " 5) configure network forwarding over Ethernet (DHCP)" + echo " 6) configure network forwarding over WiFi (static IP)" + echo "MISCELLANEOUS" + echo " 7) reserve SCSI IDs" } +# parse arguments +while [ "$1" != "" ]; do + PARAM=`echo $1 | awk -F= '{print $1}'` + VALUE=`echo $1 | awk -F= '{print $2}'` + case $PARAM in + -c | --connect_type) + CONNECT_TYPE=$VALUE + ;; + -r | --run_choice) + RUN_CHOICE=$VALUE + ;; + *) + echo "ERROR: unknown parameter \"$PARAM\"" + exit 1 + ;; + esac + case $VALUE in + FULLSPEC | STANDARD | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7) + ;; + *) + echo "ERROR: unknown option \"$VALUE\"" + exit 1 + ;; + esac + shift +done showRaSCSILogo initialChecks -if [ -z "${1}" ]; then # $1 is unset, show menu + +if [ -z "${RUN_CHOICE}" ]; then # RUN_CHOICE is unset, show menu showMenu readChoice else - runChoice "$1" + runChoice "$RUN_CHOICE" fi diff --git a/src/raspberrypi/devices/cfilesystem.cpp b/src/raspberrypi/devices/cfilesystem.cpp index bc939147..c1b3e06a 100644 --- a/src/raspberrypi/devices/cfilesystem.cpp +++ b/src/raspberrypi/devices/cfilesystem.cpp @@ -154,45 +154,45 @@ void Human68k::namests_t::GetCopyFilename(BYTE* szFilename) const for (i = 0; i < 8; i++) { BYTE c = name[i]; if (c == ' ') { - // ファイル名中にスペースが出現した場合、以降のエントリが続いているかどうか確認 - /// @todo 8+3文字とTewntyOne互換モードで動作を変えるべき - // add[0] が有効な文字なら続ける + // Check that the file name continues after a space is detected + /// TODO: Should change this function to be compatible with 8+3 chars and TwentyOne + // Continue if add[0] is a valid character if (add[0] != '\0') goto next_name; - // name[i] より後に空白以外の文字が存在するなら続ける + // Continue if a non-space character exists after name[i] for (size_t j = i + 1; j < 8; j++) { if (name[j] != ' ') goto next_name; } - // ファイル名終端なら転送終了 + // Exit if the file name ends break; } next_name: *p++ = c; } - // 全ての文字を読み込むと、ここで i >= 8 となる + // At this point, the number of read characters becomes i >= 8 - // ファイル名本体が8文字以上なら追加部分も加える + // If the body of the file name exceeds 8 characters, add the extraneous part if (i >= 8) { - // ファイル名追加部分転送 + // Transfer the extraneous part for (i = 0; i < 10; i++) { BYTE c = add[i]; if (c == '\0') break; *p++ = c; } - // 全ての文字を読み込むと、ここで i >= 10 となる + // At this point, the number of read characters becomes i >= 10 } - // 拡張子が存在する場合は転送 + // Transfer the file extension if it exists if (ext[0] != ' ' || ext[1] != ' ' || ext[2] != ' ') { *p++ = '.'; for (i = 0; i < 3; i++) { BYTE c = ext[i]; if (c == ' ') { - // 拡張子中にスペースが出現した場合、以降のエントリが続いているかどうか確認 - /// @todo 8+3文字とTewntyOne互換モードで動作を変えるべき - // ext[i] より後に空白以外の文字が存在するなら続ける + // Check that the file extension continues after a space is detected + /// TODO: Should change this function to be compatible with 8+3 chars and TwentyOne + // Continue if a non-space character exists after ext[i] for (size_t j = i + 1; j < 3; j++) { if (ext[j] != ' ') goto next_ext; @@ -216,14 +216,8 @@ void Human68k::namests_t::GetCopyFilename(BYTE* szFilename) const // //=========================================================================== -//--------------------------------------------------------------------------- -// -// Default constructor -// -//--------------------------------------------------------------------------- CHostDrv::CHostDrv() { - // Initialization m_bWriteProtect = FALSE; m_bEnable = FALSE; m_capCache.sectors = 0; @@ -233,11 +227,6 @@ CHostDrv::CHostDrv() m_nRing = 0; } -//--------------------------------------------------------------------------- -// -// Final destructor -// -//--------------------------------------------------------------------------- CHostDrv::~CHostDrv() { CHostPath* p; @@ -291,7 +280,7 @@ void CHostDrv::Init(const TCHAR* szBase, DWORD nFlag) } else { pClear = NULL; } - if ((0x80 <= c && c <= 0x9F) || 0xE0 <= c) { // 厳密には 0x81~0x9F 0xE0~0xEF + if ((0x80 <= c && c <= 0x9F) || 0xE0 <= c) { // To be precise: 0x81~0x9F 0xE0~0xEF p++; if (*p == _T('\0')) break; @@ -424,17 +413,17 @@ void CHostDrv::GetVolume(TCHAR* szLabel) //--------------------------------------------------------------------------- // -/// キャッシュからボリュームラベルを取得 +/// Get volume label from cache /// -/// キャッシュされているボリュームラベル情報を転送する。 -/// キャッシュ内容が有効ならTRUEを、無効ならFALSEを返す。 +/// Transfer the cached volume label information. +/// If the cache contents are valid return TRUE, if invalid return FALSE. // //--------------------------------------------------------------------------- BOOL CHostDrv::GetVolumeCache(TCHAR* szLabel) const { ASSERT(szLabel); - // 内容を転送 + // Transfer contents strcpy(szLabel, m_szVolumeCache); return m_bVolumeCache; @@ -442,7 +431,7 @@ BOOL CHostDrv::GetVolumeCache(TCHAR* szLabel) const //--------------------------------------------------------------------------- // -/// 容量の取得 +/// Get Capacity // //--------------------------------------------------------------------------- DWORD CHostDrv::GetCapacity(Human68k::capacity_t* pCapacity) @@ -459,18 +448,18 @@ DWORD CHostDrv::GetCapacity(Human68k::capacity_t* pCapacity) clusters = 0xFFFF; sectors = 64; - // パラメータ範囲想定 + // Estimated parameter range ASSERT(freearea <= 0xFFFF); ASSERT(clusters <= 0xFFFF); ASSERT(sectors <= 64); - // キャッシュ更新 + // Update cache m_capCache.freearea = (WORD)freearea; m_capCache.clusters = (WORD)clusters; m_capCache.sectors = (WORD)sectors; m_capCache.bytes = 512; - // 内容を転送 + // Transfer contents memcpy(pCapacity, &m_capCache, sizeof(m_capCache)); return nFree; @@ -478,17 +467,17 @@ DWORD CHostDrv::GetCapacity(Human68k::capacity_t* pCapacity) //--------------------------------------------------------------------------- // -/// キャッシュから容量を取得 +/// Get capacity from the cache /// -/// キャッシュされている容量情報を転送する。 -/// キャッシュ内容が有効ならTRUEを、無効ならFALSEを返す。 +/// Transfer the capacity data stored in cache. +/// If the contents of the cache is valid return TRUE, is invalid return FALSE. // //--------------------------------------------------------------------------- BOOL CHostDrv::GetCapacityCache(Human68k::capacity_t* pCapacity) const { ASSERT(pCapacity); - // 内容を転送 + // Transfer contents memcpy(pCapacity, &m_capCache, sizeof(m_capCache)); return m_capCache.sectors != 0; @@ -496,7 +485,7 @@ BOOL CHostDrv::GetCapacityCache(Human68k::capacity_t* pCapacity) const //--------------------------------------------------------------------------- // -/// 全てのキャッシュを更新する +/// Update all cache // //--------------------------------------------------------------------------- void CHostDrv::CleanCache() @@ -511,7 +500,7 @@ void CHostDrv::CleanCache() //--------------------------------------------------------------------------- // -/// 指定されたパスのキャッシュを更新する +/// Update the cache for the specified path // //--------------------------------------------------------------------------- void CHostDrv::CleanCache(const BYTE* szHumanPath) @@ -529,7 +518,7 @@ void CHostDrv::CleanCache(const BYTE* szHumanPath) //--------------------------------------------------------------------------- // -/// 指定されたパス以下のキャッシュを全て更新する +/// Update the cache below and including the specified path // //--------------------------------------------------------------------------- void CHostDrv::CleanCacheChild(const BYTE* szHumanPath) @@ -548,7 +537,7 @@ void CHostDrv::CleanCacheChild(const BYTE* szHumanPath) //--------------------------------------------------------------------------- // -/// 指定されたパスのキャッシュを削除する +/// Delete the cache for the specified path // //--------------------------------------------------------------------------- void CHostDrv::DeleteCache(const BYTE* szHumanPath) @@ -567,18 +556,18 @@ void CHostDrv::DeleteCache(const BYTE* szHumanPath) //--------------------------------------------------------------------------- // -/// 指定されたパスがキャッシュされているか検索する +/// Check if the specified path is cached /// -/// 所有するキャシュバッファの中から完全一致で検索し、見つかればその名称を返す。 -/// ファイル名を除外しておくこと。 -/// 必ず上位で排他制御を行なうこと。 +/// Check if whether it is a perfect match with the cache buffer, and return the name if found. +/// File names are excempted. +/// Make sure to lock from the top. // //--------------------------------------------------------------------------- CHostPath* CHostDrv::FindCache(const BYTE* szHuman) { ASSERT(szHuman); - // 所持している全てのファイル名の中から完全一致するものを検索 + // Find something that matches perfectly with either of the stored file names for (CHostPath* p = (CHostPath*)m_cRing.Next(); p != &m_cRing;) { if (p->isSameHuman(szHuman)) return p; @@ -590,11 +579,11 @@ CHostPath* CHostDrv::FindCache(const BYTE* szHuman) //--------------------------------------------------------------------------- // -/// キャッシュ情報を元に、ホスト側の名称を獲得する +/// Get the host side name from cached data. /// -/// パスがキャッシュにあるか確認。なければエラー。 -/// 見つかったキャッシュの更新チェック。更新が必要ならエラー。 -/// 必ず上位で排他制御を行なうこと。 +/// Confirm if the path is cached. If not, throw an error. +/// Carry out an update check on found cache. If an update is needed, throw an error. +/// Make sure to lock from the top. // //--------------------------------------------------------------------------- CHostPath* CHostDrv::CopyCache(CHostFiles* pFiles) @@ -602,21 +591,21 @@ CHostPath* CHostDrv::CopyCache(CHostFiles* pFiles) ASSERT(pFiles); ASSERT(strlen((const char*)pFiles->GetHumanPath()) < HUMAN68K_PATH_MAX); - // キャッシュ検索 + // Find in cache CHostPath* pPath = FindCache(pFiles->GetHumanPath()); if (pPath == NULL) { - return NULL; // エラー: キャッシュなし + return NULL; // Error: No cache } - // リング先頭へ移動 + // Move to the beginning of the ring pPath->Insert(&m_cRing); - // キャッシュ更新チェック + // Cache update check if (pPath->isRefresh()) { - return NULL; // エラー: キャッシュ更新が必要 + return NULL; // Error: Cache update is required } - // ホスト側のパス名を保存 + // Store the host side path pFiles->SetResult(pPath->GetHost()); return pPath; @@ -624,22 +613,22 @@ CHostPath* CHostDrv::CopyCache(CHostFiles* pFiles) //--------------------------------------------------------------------------- // -/// ホスト側の名称の構築に必要な情報をすべて取得する +/// Get all the data required for a host side name structure /// -/// ファイル名は省略可能。(普通は指定しない) -/// 必ず上位で排他制御を行なうこと。 -/// ベースパス末尾にパス区切り文字をつけないよう注意。 -/// ファイルアクセスが多発する可能性があるときは、VMスレッドの動作を開始させる。 +/// File names can be abbreviated. (Normally not selected) +/// Make sure to lock from the top. +/// Be careful not to append the path separator char to the end of the base path. +/// Initiate VM threads when there is a chance of multiple file accesses. /// -/// 使いかた: -/// CopyCache()してエラーの場合はMakeCache()する。必ず正しいホスト側のパスが取得できる。 +/// How to use: +/// When CopyCache() throws an error execute MakeCache(). It ensures you get a correct host side path. /// -/// ファイル名とパス名をすべて分離する。 -/// 上位ディレクトリから順に、キャッシュされているかどうか確認。 -/// キャッシュされていれば破棄チェック。破棄した場合未キャッシュ扱いとなる。 -/// キャッシュされていなければキャッシュを構築。 -/// 順番にすべてのディレクトリ・ファイル名に対して行ない終了。 -/// エラーが発生した場合はNULLとなる。 +/// Split all file names and path names. +/// Confirm that it's cached from the top level directory down. +/// If it's cached, do a destruction check. If it was destroyed, treat it as uncached. +/// If it isn't cached, build cache. +/// Exit after processing all directory and file names in order. +/// Make it NULL is an error is thrown. // //--------------------------------------------------------------------------- CHostPath* CHostDrv::MakeCache(CHostFiles* pFiles) @@ -650,7 +639,7 @@ CHostPath* CHostDrv::MakeCache(CHostFiles* pFiles) ASSERT(m_szBase); ASSERT(strlen(m_szBase) < FILEPATH_MAX); - BYTE szHumanPath[HUMAN68K_PATH_MAX]; // ルートから順にパス名が入る + BYTE szHumanPath[HUMAN68K_PATH_MAX]; // Path names are entered in order from the route szHumanPath[0] = '\0'; size_t nHumanPath = 0; @@ -661,35 +650,35 @@ CHostPath* CHostDrv::MakeCache(CHostFiles* pFiles) CHostPath* pPath; const BYTE* p = pFiles->GetHumanPath(); for (;;) { - // パス区切りを追加 + // Add path separators if (nHumanPath + 1 >= HUMAN68K_PATH_MAX) - return NULL; // エラー: Human68kパスが長すぎる + return NULL; // Error: The Human68k path is too long szHumanPath[nHumanPath++] = '/'; szHumanPath[nHumanPath] = '\0'; if (nHostPath + 1 >= FILEPATH_MAX) - return NULL; // エラー: ホスト側のパスが長すぎる + return NULL; // Error: The host side path is too long szHostPath[nHostPath++] = _T('/'); szHostPath[nHostPath] = _T('\0'); - // ファイルいっこいれる - BYTE szHumanFilename[24]; // ファイル名部分 + // Insert one file + BYTE szHumanFilename[24]; // File name part p = SeparateCopyFilename(p, szHumanFilename); if (p == NULL) - return NULL; // エラー: ファイル名読み込み失敗 + return NULL; // Error: Failed to read file name size_t n = strlen((const char*)szHumanFilename); if (nHumanPath + n >= HUMAN68K_PATH_MAX) - return NULL; // エラー: Human68kパスが長すぎる + return NULL; // Error: The Human68k path is too long - // 該当パスがキャッシュされているか? + // Is the relevant path cached? pPath = FindCache(szHumanPath); if (pPath == NULL) { - // キャッシュ最大数チェック + // Check for max number of cache if (m_nRing >= XM6_HOST_DIRENTRY_CACHE_MAX) { - // 最も古いキャッシュを破棄して再利用 + // Destroy the oldest cache and reuse it pPath = (CHostPath*)m_cRing.Prev(); - pPath->Clean(); // 全ファイル解放 更新チェック用ハンドルも解放 + pPath->Clean(); // Release all files. Release update check handlers. } else { - // 新規登録 + // Register new pPath = new CHostPath; ASSERT(pPath); m_nRing++; @@ -697,43 +686,42 @@ CHostPath* CHostDrv::MakeCache(CHostFiles* pFiles) pPath->SetHuman(szHumanPath); pPath->SetHost(szHostPath); - // 状態更新 + // Update status pPath->Refresh(); } - // キャッシュ更新チェック + // Cache update check if (pPath->isRefresh()) { - // 更新 Update(); - // 状態更新 + // Update status pPath->Refresh(); } - // リング先頭へ + // Into the beginning of the ring pPath->Insert(&m_cRing); - // ファイル名がなければここで終了 + // Exit if there is not file name if (n == 0) break; - // 次のパスを検索 - // パスの途中ならディレクトリかどうか確認 + // Find the next path + // Confirm if directory from the middle of the path const CHostFilename* pFilename; if (*p != '\0') pFilename = pPath->FindFilename(szHumanFilename, Human68k::AT_DIRECTORY); else pFilename = pPath->FindFilename(szHumanFilename); if (pFilename == NULL) - return NULL; // エラー: 途中のパス名/ファイル名が見つからない + return NULL; // Error: Could not find path or file names in the middle - // パス名を連結 + // Link path name strcpy((char*)szHumanPath + nHumanPath, (const char*)szHumanFilename); nHumanPath += n; n = strlen(pFilename->GetHost()); if (nHostPath + n >= FILEPATH_MAX) - return NULL; // エラー: ホスト側のパスが長すぎる + return NULL; // Error: Host side path is too long strcpy(szHostPath + nHostPath, pFilename->GetHost()); nHostPath += n; @@ -742,7 +730,7 @@ CHostPath* CHostDrv::MakeCache(CHostFiles* pFiles) break; } - // ホスト側のパス名を保存 + // Store the host side path name pFiles->SetResult(szHostPath); return pPath; @@ -750,52 +738,50 @@ CHostPath* CHostDrv::MakeCache(CHostFiles* pFiles) //--------------------------------------------------------------------------- // -/// ホスト側の名称を検索 (パス名+ファイル名(省略可)+属性) +/// Find host side name (path name + file name (can be abbeviated) + attribute) /// -/// あらかじめ全てのHuman68k用パラメータを設定しておくこと。 +/// Set all Human68k parameters once more. // //--------------------------------------------------------------------------- BOOL CHostDrv::Find(CHostFiles* pFiles) { ASSERT(pFiles); - // 排他制御開始 Lock(); - // パス名獲得およびキャッシュ構築 + // Get path name and build cache CHostPath* pPath = CopyCache(pFiles); if (pPath == NULL) { pPath = MakeCache(pFiles); if (pPath == NULL) { Unlock(); CleanCache(); - return FALSE; // エラー: キャッシュ構築失敗 + return FALSE; // Error: Failed to build cache } } - // ホスト側のパス名を保存 + // Store host side path pFiles->SetResult(pPath->GetHost()); - // パス名のみなら終了 + // Exit if only path name if (pFiles->isPathOnly()) { Unlock(); - return TRUE; // 正常終了: パス名のみ + return TRUE; // Normal exit: only path name } - // ファイル名検索 + // Find file name const CHostFilename* pFilename = pFiles->Find(pPath); if (pFilename == NULL) { Unlock(); - return FALSE; // エラー: ファイル名が獲得できません + return FALSE; // Error: Could not get file name } - // Human68k側の検索結果保存 + // Store the Human68k side search results pFiles->SetEntry(pFilename); - // ホスト側のフルパス名保存 + // Store the host side full path name pFiles->AddResult(pFilename->GetHost()); - // 排他制御終了 Unlock(); return TRUE; @@ -803,15 +789,10 @@ BOOL CHostDrv::Find(CHostFiles* pFiles) //=========================================================================== // -// ディレクトリエントリ ファイル名 +// Directory entry: File name // //=========================================================================== -//--------------------------------------------------------------------------- -// -/// デフォルトコンストラクタ -// -//--------------------------------------------------------------------------- CHostFilename::CHostFilename() { m_bCorrect = FALSE; @@ -821,7 +802,7 @@ CHostFilename::CHostFilename() //--------------------------------------------------------------------------- // -/// ホスト側の名称を設定 +/// Set host side name // //--------------------------------------------------------------------------- void CHostFilename::SetHost(const TCHAR* szHost) @@ -834,7 +815,7 @@ void CHostFilename::SetHost(const TCHAR* szHost) //--------------------------------------------------------------------------- // -/// Human68k側のファイル名要素をコピー +/// Copy the Human68k file name elements // //--------------------------------------------------------------------------- BYTE* CHostFilename::CopyName(BYTE* pWrite, const BYTE* pFirst, const BYTE* pLast) // static @@ -852,15 +833,15 @@ BYTE* CHostFilename::CopyName(BYTE* pWrite, const BYTE* pFirst, const BYTE* pLas //--------------------------------------------------------------------------- // -/// Human68k側の名称を変換 +/// Convert the Human68k side name /// -/// あらかじめSetHost()を実行しておくこと。 -/// 18+3の命名規則に従った名前変換を行なう。 -/// ファイル名先頭および末尾の空白は、Human68kで扱えないため自動的に削除される。 -/// ディレクトリエントリの名前部分を、ファイル名変換時の拡張子の位置情報を使って生成する。 -/// その後、ファイル名の異常判定を行なう。(スペース8文字だけのファイル名など) -/// ファイル名の重複判定は行なわないので注意。これらの判定は上位クラスで行なう。 -/// TwentyOne version 1.36c modified +14 patchlevel9以降の拡張子規則に対応させる。 +/// Once more, execute SetHost(). +/// Carry out name conversion to the 18+3 standard. +/// Automatically delete spaces in the beginning and end of the names, since Human68k can't handle them. +/// The directory entry name segment is created at the time of conversion using knowledge of the location of the file name extension. +/// Afterwards, a file name validity check is performed. (Ex. file names consisting of 8 spaces only.) +/// No file name duplication check is performed so be careful. Such validation is carried out in classes higher up. +/// Adhers to the naming standards of: TwentyOne version 1.36c modified +14 patchlevel9 or later // //--------------------------------------------------------------------------- void CHostFilename::ConvertHuman(int nCount) @@ -868,10 +849,10 @@ void CHostFilename::ConvertHuman(int nCount) char szHost[FILEPATH_MAX]; - // 特殊ディレクトリ名の場合は変換しない + // Don't do conversion for special directory names if (m_szHost[0] == _T('.') && (m_szHost[1] == _T('\0') || (m_szHost[1] == _T('.') && m_szHost[2] == _T('\0')))) { - strcpy((char*)m_szHuman, m_szHost); /// @warning Unicode時要修正 → 済 + strcpy((char*)m_szHuman, m_szHost); m_bCorrect = TRUE; m_pszHumanLast = m_szHuman + strlen((const char*)m_szHuman); @@ -879,17 +860,17 @@ void CHostFilename::ConvertHuman(int nCount) return; } - size_t nMax = 18; // ベース部分(ベース名と拡張子名)のバイト数 + size_t nMax = 18; // Number of bytes for the base segment (base name and extension) DWORD nOption = CFileSys::GetFileOption(); if (nOption & WINDRV_OPT_CONVERT_LENGTH) nMax = 8; - // ベース名部分の補正準備 + // Preparations to adjust the base name segment BYTE szNumber[8]; BYTE* pNumber = NULL; if (nCount >= 0) { pNumber = &szNumber[8]; - for (DWORD i = 0; i < 5; i++) { // 最大5+1桁まで (ベース名先頭2バイトは必ず残す) + for (DWORD i = 0; i < 5; i++) { // Max 5+1 digits (always leave the first 2 bytes of the base name) int n = nCount % 36; nMax--; pNumber--; @@ -906,8 +887,7 @@ void CHostFilename::ConvertHuman(int nCount) *pNumber = c; } - // 文字変換 - /// @warning Unicode未対応。いずれUnicodeの世界に飮まれた時はここで変換を行なう → 済 + // Char conversion BYTE szHuman[FILEPATH_MAX]; const BYTE* pFirst = szHuman; const BYTE* pLast; @@ -928,7 +908,7 @@ void CHostFilename::ConvertHuman(int nCount) if (nOption & WINDRV_OPT_CONVERT_SPACE) c = '_'; else if (pWrite == szHuman) - continue; // 先頭の空白は無視 + continue; // Ignore spaces in the beginning break; case '=': case '+': @@ -951,7 +931,7 @@ void CHostFilename::ConvertHuman(int nCount) c = '_'; break; case '.': - if (pRead - 1 == pPeriod) { // Human68k拡張子は例外とする + if (pRead - 1 == pPeriod) { // Make exception for Human68k extensions pExt = pWrite; break; } @@ -976,76 +956,76 @@ void CHostFilename::ConvertHuman(int nCount) pLast = pWrite - 1; } - // 拡張子補正 + // Adjust extensions if (pExt) { - // 末尾の空白を削除する + // Delete spaces at the end while (pExt < pLast - 1 && *(pLast - 1) == ' ') { pLast--; BYTE* p = (BYTE*)pLast; *p = '\0'; } - // 変換後に実体がなくなった場合は削除 + // Delete if the file name disappeared after conversion if (pExt + 1 >= pLast) { pLast = pExt; BYTE* p = (BYTE*)pLast; - *p = '\0'; // 念のため + *p = '\0'; // Just in case } } else { pExt = pLast; } - // 登場人物紹介 + // Introducing the cast of characters // - // pFirst: 俺はリーダー。ファイル名先頭 - // pCut: 通称フェイス。最初のピリオドの出現位置 その後ベース名終端位置となる - // pSecond: よぉおまちどう。俺様こそマードック。拡張子名の開始位置。だから何。 - // pExt: B・A・バラカス。Human68k拡張子の天才だ。でも、3文字より長い名前は勘弁な。 - // 最後のピリオドの出現位置 該当しなければpLastと同じ値 + // pFirst: I'm the glorious leader. The start of the file name. + // pCut: A.k.a. Phase. Location of the initial period. Afterwards becomes the end of the base name. + // pSecond: Hello there! I'm the incredible Murdock. The start of the file name extension. What's it to you? + // pExt: B.A. Baracus. The Human68k extension genius. But don't you dare giving me more than 3 chars, fool. + // The location of the final period. If not applicable, gets the same value as pLast. // // ↓pFirst ↓pStop ↓pSecond ← ↓pExt // T h i s _ i s _ a . V e r y . L o n g . F i l e n a m e . t x t \0 - // ↑pCut ← ↑pCut初期位置 ↑pLast + // ↑pCut ← ↑pCut initial location ↑pLast // - // 上記の場合、変換後は This.Long.Filename.txt となる + // The above example becomes "This.Long.Filename.txt" after conversion - // 1文字目判定 + // Evaluate first char const BYTE* pCut = pFirst; - const BYTE* pStop = pExt - nMax; // 拡張子名は最大17バイトとする(ベース名を残す) + const BYTE* pStop = pExt - nMax; // Allow for up to 17 bytes for extension (leave base name) if (pFirst < pExt) { - pCut++; // 必ず1バイトはベース名を使う + pCut++; // 1 byte always uses the base name BYTE c = *pFirst; - if ((0x80 <= c && c <= 0x9F) || 0xE0 <= c) { // 厳密には 0x81~0x9F 0xE0~0xEF - pCut++; // ベース名 最小2バイト - pStop++; // 拡張子名 最大16バイト + if ((0x80 <= c && c <= 0x9F) || 0xE0 <= c) { // Specifically 0x81~0x9F 0xE0~0xEF + pCut++; // Base name. At least 2 bytes. + pStop++; // File extension. Max 16 bytes. } } if (pStop < pFirst) pStop = pFirst; - // ベース名判定 - pCut = (BYTE*)strchr((const char*)pCut, '.'); // SJIS2バイト目は必ず0x40以上なので問題ない + // Evaluate base name + pCut = (BYTE*)strchr((const char*)pCut, '.'); // The 2nd byte of Shift-JIS is always 0x40 or higher, so this is ok if (pCut == NULL) pCut = pLast; if ((size_t)(pCut - pFirst) > nMax) - pCut = pFirst + nMax; // 後ほどSJIS2バイト判定/補正を行なう ここで判定してはいけない + pCut = pFirst + nMax; // Execute Shift-JIS 2 byte evaluation/adjustment later. Not allowed to do it here. - // 拡張子名判定 + // Evaluate extension const BYTE* pSecond = pExt; const BYTE* p; for (p = pExt - 1; pStop < p; p--) { if (*p == '.') - pSecond = p; // SJIS2バイト目は必ず0x40以上なので問題ない + pSecond = p; // The 2nd byte of Shift-JIS is always 0x40 or higher, so this is ok } - // ベース名を短縮 - size_t nExt = pExt - pSecond; // 拡張子名部分の長さ + // Shorten base name + size_t nExt = pExt - pSecond; // Length of extension segment if ((size_t)(pCut - pFirst) + nExt > nMax) pCut = pFirst + nMax - nExt; - // 2バイト文字の途中ならさらに短縮 + // If in the middle of a 2 byte char, shorten even further for (p = pFirst; p < pCut; p++) { BYTE c = *p; - if ((0x80 <= c && c <= 0x9F) || 0xE0 <= c) { // 厳密には 0x81~0x9F 0xE0~0xEF + if ((0x80 <= c && c <= 0x9F) || 0xE0 <= c) { // Specifically 0x81~0x9F 0xE0~0xEF p++; if (p >= pCut) { pCut--; @@ -1054,31 +1034,31 @@ void CHostFilename::ConvertHuman(int nCount) } } - // 名前の結合 + // Joining the name BYTE* pWrite = m_szHuman; - pWrite = CopyName(pWrite, pFirst, pCut); // ベース名を転送 + pWrite = CopyName(pWrite, pFirst, pCut); // Transfer the base name if (pNumber) - pWrite = CopyName(pWrite, pNumber, &szNumber[8]); // 補正文字を転送 - pWrite = CopyName(pWrite, pSecond, pExt); // 拡張子名を転送 - m_pszHumanExt = pWrite; // 拡張子位置保存 - pWrite = CopyName(pWrite, pExt, pLast); // Human68k拡張子を転送 - m_pszHumanLast = pWrite; // 終端位置保存 + pWrite = CopyName(pWrite, pNumber, &szNumber[8]); // Transfer the adjustment char + pWrite = CopyName(pWrite, pSecond, pExt); // Transfer the extension name + m_pszHumanExt = pWrite; // Store the extention position + pWrite = CopyName(pWrite, pExt, pLast); // Transfer the Human68k extension + m_pszHumanLast = pWrite; // Store the end position *pWrite = '\0'; - // 変換結果の確認 + // Confirm the conversion results m_bCorrect = TRUE; - // ファイル名本体が存在しなければ不合格 + // Fail if the base file name does not exist if (m_pszHumanExt <= m_szHuman) m_bCorrect = FALSE; - // ファイル名本体が1文字以上でかつ空白で終了していれば不合格 - // ファイル名本体が8文字以上の場合、理論上は空白での終了が表現可 - // 能だが、Human68kでは正しく扱えないため、これも不合格とする + // Fail if the base file name is more than 1 char and ends with a space + // While it is theoretically valid to have a base file name exceed 8 chars, + // Human68k is unable to handle it, so failing this case too. else if (m_pszHumanExt[-1] == ' ') m_bCorrect = FALSE; - // 変換結果が特殊ディレクトリ名と同じなら不合格 + // Fail if the conversion result is the same as a special directory name if (m_szHuman[0] == '.' && (m_szHuman[1] == '\0' || (m_szHuman[1] == '.' && m_szHuman[2] == '\0'))) m_bCorrect = FALSE; @@ -1086,9 +1066,9 @@ void CHostFilename::ConvertHuman(int nCount) //--------------------------------------------------------------------------- // -/// Human68k側の名称を複製 +/// Human68k side name duplication /// -/// ファイル名部分の情報を複製し、ConvertHuman()相当の初期化動作を行なう。 +/// Duplicates the file name segment data, then executes the correspoding initialization with ConvertHuman(). // //--------------------------------------------------------------------------- void CHostFilename::CopyHuman(const BYTE* szHuman) @@ -1104,15 +1084,15 @@ void CHostFilename::CopyHuman(const BYTE* szHuman) //--------------------------------------------------------------------------- // -/// Human68kディレクトリエントリを設定 +/// Set Human68k directory entry /// -/// ConvertHuman()で設定済みのファイル名をディレクトリエントリに反映する。 +/// Apply the set file name to the directory entry with ConvertHuman(). // //--------------------------------------------------------------------------- void CHostFilename::SetEntryName() { - // ファイル名設定 + // Set file name BYTE* p = m_szHuman; size_t i; for (i = 0; i < 8; i++) { @@ -1141,18 +1121,18 @@ void CHostFilename::SetEntryName() //--------------------------------------------------------------------------- // -/// Human68k側の名称が加工されたか調査 +/// Investigate if the Human68k side name has been processed // //--------------------------------------------------------------------------- BOOL CHostFilename::isReduce() const { - return strcmp((char *)m_szHost, (const char*)m_szHuman) != 0; /// @warning Unicode時要修正 → 済 + return strcmp((char *)m_szHost, (const char*)m_szHuman) != 0; } //--------------------------------------------------------------------------- // -/// Human68kディレクトリエントリの属性判定 +/// Evaluate Human68k directory entry attribute // //--------------------------------------------------------------------------- BOOL CHostFilename::CheckAttribute(DWORD nHumanAttribute) const @@ -1167,26 +1147,26 @@ BOOL CHostFilename::CheckAttribute(DWORD nHumanAttribute) const //--------------------------------------------------------------------------- // -/// Human68kファイル名から拡張子を分離 +/// Split the extension from Human68k file name // //--------------------------------------------------------------------------- const BYTE* CHostFilename::SeparateExt(const BYTE* szHuman) // static { - // ファイル名の長さを獲得 + // Obtain the file name length size_t nLength = strlen((const char*)szHuman); const BYTE* pFirst = szHuman; const BYTE* pLast = pFirst + nLength; - // Human68k拡張子の位置を確認 - const BYTE* pExt = (BYTE*)strrchr((const char*)pFirst, '.'); // SJIS2バイト目は必ず0x40以上なので問題ない + // Confirm the position of the Human68k extension + const BYTE* pExt = (BYTE*)strrchr((const char*)pFirst, '.'); // The 2nd byte of Shift-JIS is always 0x40 or higher, so this is ok if (pExt == NULL) pExt = pLast; - // ファイル名が20~22文字かつ19文字目が'.'かつ'.'で終了というパターンを特別扱いする + // Special handling of the pattern where the file name is 20~22 chars, and the 19th char is '.' or ends with '.' if (20 <= nLength && nLength <= 22 && pFirst[18] == '.' && pFirst[nLength - 1] == '.') pExt = pFirst + 18; - // 拡張子の文字数を計算 (-1:なし 0:ピリオドだけ 1~3:Human68k拡張子 4以上:拡張子名) + // Calculate the number of chars in the extension (-1:None 0:Only period 1~3:Human68k extension 4 or above:extension name) size_t nExt = pLast - pExt - 1; - // '.' が文字列先頭以外に存在して、かつ1~3文字の場合のみ拡張子とみなす + // Consider it an extension only when '.' is anywhere except the beginning of the string, and between 1~3 chars long if (pExt == pFirst || nExt < 1 || nExt > 3) pExt = pLast; @@ -1195,17 +1175,12 @@ const BYTE* CHostFilename::SeparateExt(const BYTE* szHuman) // static //=========================================================================== // -// ディレクトリエントリ パス名 +// Directory entry: path name // //=========================================================================== -DWORD CHostPath::g_nId; ///< 識別ID生成用カウンタ +DWORD CHostPath::g_nId; ///< Identifier creation counter -//--------------------------------------------------------------------------- -// -/// デフォルトコンストラクタ -// -//--------------------------------------------------------------------------- CHostPath::CHostPath() { m_bRefresh = TRUE; @@ -1213,16 +1188,11 @@ CHostPath::CHostPath() m_tBackup = FALSE; #ifdef _DEBUG - // 必ず値が更新されるので初期化不要 (デバッグ時の初期動作確認用) + // Initialization is not required because this value always gets updated (used for debugging or initialization operation) m_nId = 0; #endif // _DEBUG } -//--------------------------------------------------------------------------- -// -/// デストラクタ final -// -//--------------------------------------------------------------------------- CHostPath::~CHostPath() { Clean(); @@ -1230,11 +1200,11 @@ CHostPath::~CHostPath() //--------------------------------------------------------------------------- // -/// ファイル名領域確保 +/// File name memory allocation /// -/// ほとんどのケースでは、ホスト側ファイル名の長さはバッファ最大長に -/// 比べて非常に短い。さらにファイル名は大量に生成される可能性がある。 -/// そのため文字数に応じた可変長で確保する。 +/// In most cases, the length of the host side file name is way shorter +/// than the size of the buffer. In addition, file names may be created in huge volumes. +/// Therefore, allocate variable lengths that correspond to the number of chars. // //--------------------------------------------------------------------------- CHostPath::ring_t* CHostPath::Alloc(size_t nLength) // static @@ -1245,14 +1215,14 @@ CHostPath::ring_t* CHostPath::Alloc(size_t nLength) // static ring_t* p = (ring_t*)malloc(n); ASSERT(p); - p->r.Init(); // 榛名は大丈夫です! + p->r.Init(); // This is nothing to worry about! return p; } //--------------------------------------------------------------------------- // -// ファイル名領域解放 +// Release file name allocations // //--------------------------------------------------------------------------- void CHostPath::Free(ring_t* pRing) // static @@ -1265,7 +1235,7 @@ void CHostPath::Free(ring_t* pRing) // static //--------------------------------------------------------------------------- // -/// 再利用のための初期化 +/// Initialize for reuse // //--------------------------------------------------------------------------- void CHostPath::Clean() @@ -1273,7 +1243,7 @@ void CHostPath::Clean() Release(); - // 全ファイル名を解放 + // Release all file names ring_t* p; while ((p = (ring_t*)m_cRing.Next()) != (ring_t*)&m_cRing) { Free(p); @@ -1282,7 +1252,7 @@ void CHostPath::Clean() //--------------------------------------------------------------------------- // -/// Human68k側の名称を直接指定する +/// Specify Human68k side names directly // //--------------------------------------------------------------------------- void CHostPath::SetHuman(const BYTE* szHuman) @@ -1295,7 +1265,7 @@ void CHostPath::SetHuman(const BYTE* szHuman) //--------------------------------------------------------------------------- // -/// ホスト側の名称を直接指定する +/// Specify host side names directly // //--------------------------------------------------------------------------- void CHostPath::SetHost(const TCHAR* szHost) @@ -1308,7 +1278,7 @@ void CHostPath::SetHost(const TCHAR* szHost) //--------------------------------------------------------------------------- // -/// 文字列比較 (ワイルドカード対応) +/// Compare arrays (supports wildcards) // //--------------------------------------------------------------------------- int CHostPath::Compare(const BYTE* pFirst, const BYTE* pLast, const BYTE* pBufFirst, const BYTE* pBufLast) @@ -1318,59 +1288,59 @@ int CHostPath::Compare(const BYTE* pFirst, const BYTE* pLast, const BYTE* pBufFi ASSERT(pBufFirst); ASSERT(pBufLast); - // 文字比較 + // Compare chars BOOL bSkip0 = FALSE; BOOL bSkip1 = FALSE; for (const BYTE* p = pFirst; p < pLast; p++) { - // 1文字読み込み + // Read 1 char BYTE c = *p; BYTE d = '\0'; if (pBufFirst < pBufLast) d = *pBufFirst++; - // 比較のための文字補正 + // Ajust char for comparison if (bSkip0 == FALSE) { - if (bSkip1 == FALSE) { // cもdも1バイト目 - if ((0x80 <= c && c <= 0x9F) || 0xE0 <= c) { // 厳密には 0x81~0x9F 0xE0~0xEF + if (bSkip1 == FALSE) { // First byte for both c and d + if ((0x80 <= c && c <= 0x9F) || 0xE0 <= c) { // Specifically 0x81~0x9F 0xE0~0xEF bSkip0 = TRUE; } - if ((0x80 <= d && d <= 0x9F) || 0xE0 <= d) { // 厳密には 0x81~0x9F 0xE0~0xEF + if ((0x80 <= d && d <= 0x9F) || 0xE0 <= d) { // Specifically 0x81~0x9F 0xE0~0xEF bSkip1 = TRUE; } if (c == d) - continue; // 高確率で判定完了する + continue; // Finishes the evaluation here with high probability if ((CFileSys::GetFileOption() & WINDRV_OPT_ALPHABET) == 0) { if ('A' <= c && c <= 'Z') - c += 'a' - 'A'; // 小文字化 + c += 'a' - 'A'; // To lower case if ('A' <= d && d <= 'Z') - d += 'a' - 'A'; // 小文字化 + d += 'a' - 'A'; // To lower case } - // バックスラッシュをスラッシュに統一して比較する + // Unify slashes and backslashes for comparison if (c == '\\') { c = '/'; } if (d == '\\') { d = '/'; } - } else { // cだけが1バイト目 - if ((0x80 <= c && c <= 0x9F) || 0xE0 <= c) { // 厳密には 0x81~0x9F 0xE0~0xEF + } else { // Only c is first byte + if ((0x80 <= c && c <= 0x9F) || 0xE0 <= c) { // Specifically 0x81~0x9F 0xE0~0xEF bSkip0 = TRUE; } bSkip1 = FALSE; } } else { - if (bSkip1 == FALSE) { // dだけが1バイト目 + if (bSkip1 == FALSE) { // Only d is first byte bSkip0 = FALSE; - if ((0x80 <= d && d <= 0x9F) || 0xE0 <= d) { // 厳密には 0x81~0x9F 0xE0~0xEF + if ((0x80 <= d && d <= 0x9F) || 0xE0 <= d) { // Specifically 0x81~0x9F 0xE0~0xEF bSkip1 = TRUE; } - } else { // cもdも2バイト目 + } else { // Second byte for both c and d bSkip0 = FALSE; bSkip1 = FALSE; } } - // 比較 + // Compare if (c == d) continue; if (c == '?') @@ -1385,78 +1355,72 @@ int CHostPath::Compare(const BYTE* pFirst, const BYTE* pLast, const BYTE* pBufFi //--------------------------------------------------------------------------- // -/// Human68k側の名称を比較する +/// Compare Human68k side name // //--------------------------------------------------------------------------- BOOL CHostPath::isSameHuman(const BYTE* szHuman) const { ASSERT(szHuman); - // 文字数計算 + // Calulate number of chars size_t nLength = strlen((const char*)m_szHuman); size_t n = strlen((const char*)szHuman); - // 文字数チェック + // Check number of chars if (nLength != n) return FALSE; - // Human68kパス名の比較 + // Compare Human68k path name return Compare(m_szHuman, m_szHuman + nLength, szHuman, szHuman + n) == 0; } -//--------------------------------------------------------------------------- -// -/// Human68k側の名称を比較する -// -//--------------------------------------------------------------------------- BOOL CHostPath::isSameChild(const BYTE* szHuman) const { ASSERT(szHuman); - // 文字数計算 + // Calulate number of chars size_t nLength = strlen((const char*)m_szHuman); size_t n = strlen((const char*)szHuman); - // 文字数チェック + // Check number of chars if (nLength < n) return FALSE; - // Human68kパス名の比較 + // Compare Human68k path name return Compare(m_szHuman, m_szHuman + n, szHuman, szHuman + n) == 0; } //--------------------------------------------------------------------------- // -/// ファイル名を検索 +/// Find file name /// -/// 所有するキャシュバッファの中から検索し、見つかればその名称を返す。 -/// パス名を除外しておくこと。 -/// 必ず上位で排他制御を行なうこと。 +/// Check if whether it is a perfect match with the cache buffer, and return the name if found. +/// Path names are excempted. +/// Make sure to lock from the top. // //--------------------------------------------------------------------------- const CHostFilename* CHostPath::FindFilename(const BYTE* szHuman, DWORD nHumanAttribute) const { ASSERT(szHuman); - // 文字数計算 + // Calulate number of chars const BYTE* pFirst = szHuman; size_t nLength = strlen((const char*)pFirst); const BYTE* pLast = pFirst + nLength; - // 所持している全てのファイル名の中から完全一致するものを検索 + // Find something that matches perfectly with either of the stored file names const ring_t* p = (ring_t*)m_cRing.Next(); for (; p != (ring_t*)&m_cRing; p = (ring_t*)p->r.Next()) { - // 属性チェック if (p->f.CheckAttribute(nHumanAttribute) == 0) continue; - // 文字数計算 + // Calulate number of chars const BYTE* pBufFirst = p->f.GetHuman(); const BYTE* pBufLast = p->f.GetHumanLast(); size_t nBufLength = pBufLast - pBufFirst; - // 文字数チェック + // Check number of chars if (nLength != nBufLength) continue; - // ファイル名チェック + // File name check if (Compare(pFirst, pLast, pBufFirst, pBufLast) == 0) return &p->f; } @@ -1466,11 +1430,11 @@ const CHostFilename* CHostPath::FindFilename(const BYTE* szHuman, DWORD nHumanAt //--------------------------------------------------------------------------- // -/// ファイル名を検索 (ワイルドカード対応) +/// Find file name (with wildcard support) /// -/// 所有するバッファの中から検索し、見つかればその名称を返す。 -/// パス名を除外しておくこと。 -/// 必ず上位で排他制御を行なうこと。 +/// Check if whether it is a perfect match with the cache buffer, and return the name if found. +/// Path names are excempted. +/// Make sure to lock from the top. // //--------------------------------------------------------------------------- const CHostFilename* CHostPath::FindFilenameWildcard(const BYTE* szHuman, DWORD nHumanAttribute, find_t* pFind) const @@ -1478,23 +1442,23 @@ const CHostFilename* CHostPath::FindFilenameWildcard(const BYTE* szHuman, DWORD ASSERT(szHuman); ASSERT(pFind); - // 検索ファイル名を本体とHuman68k拡張子に分ける + // Split the base file name and Human68k file extension const BYTE* pFirst = szHuman; const BYTE* pLast = pFirst + strlen((const char*)pFirst); const BYTE* pExt = CHostFilename::SeparateExt(pFirst); - // 開始地点へ移動 + // Move to the start position const ring_t* p = (ring_t*)m_cRing.Next(); if (pFind->count > 0) { if (pFind->id == m_nId) { - // ディレクトリエントリが同一なら、前回の位置から即継続 + // If the same directory entry, continue right away from the previous position p = pFind->pos; } else { - // 開始地点をディレクトリエントリ内容から検索する + // Find the start position in the directory entry contents DWORD n = 0; for (;; p = (ring_t*)p->r.Next()) { if (p == (ring_t*)&m_cRing) { - // 同一エントリが見つからなかった場合、回数から推定 (念のため) + // Extrapolate from the count when the same entry isn't found (just in case) p = (ring_t*)m_cRing.Next(); n = 0; for (; p != (ring_t*)&m_cRing; p = (ring_t*)p->r.Next()) { @@ -1505,7 +1469,7 @@ const CHostFilename* CHostPath::FindFilenameWildcard(const BYTE* szHuman, DWORD break; } if (p->f.isSameEntry(&pFind->entry)) { - // 同一エントリを発見 + // Same entry is found pFind->count = n; break; } @@ -1514,28 +1478,27 @@ const CHostFilename* CHostPath::FindFilenameWildcard(const BYTE* szHuman, DWORD } } - // ファイル検索 + // Find files for (; p != (ring_t*)&m_cRing; p = (ring_t*)p->r.Next()) { pFind->count++; - // 属性チェック if (p->f.CheckAttribute(nHumanAttribute) == 0) continue; - // ファイル名を本体とHuman68k拡張子に分ける + // Split the base file name and Human68k file extension const BYTE* pBufFirst = p->f.GetHuman(); const BYTE* pBufLast = p->f.GetHumanLast(); const BYTE* pBufExt = p->f.GetHumanExt(); - // 本体比較 + // Compare base file name if (Compare(pFirst, pExt, pBufFirst, pBufExt)) continue; - // Human68k拡張子比較 - // 拡張子.???の場合は、Human68k拡張子のピリオドなしにもマッチさせる + // Compare Human68k extension + // In the case of a '.???' extension, match the Human68k extension without period. if (strcmp((const char*)pExt, ".???") == 0 || Compare(pExt, pLast, pBufExt, pBufLast) == 0) { - // 次の候補のディレクトリエントリ内容を記録 + // Store the contents of the next candidate's directory entry const ring_t* pNext = (ring_t*)p->r.Next(); pFind->id = m_nId; pFind->pos = pNext; @@ -1555,7 +1518,7 @@ const CHostFilename* CHostPath::FindFilenameWildcard(const BYTE* szHuman, DWORD //--------------------------------------------------------------------------- // -/// ファイル変更が行なわれたか確認 +/// Confirm that the file update has been carried out // //--------------------------------------------------------------------------- BOOL CHostPath::isRefresh() @@ -1566,7 +1529,7 @@ BOOL CHostPath::isRefresh() //--------------------------------------------------------------------------- // -/// ASCIIソート関数 +/// ASCII sort function // //--------------------------------------------------------------------------- int AsciiSort(const dirent **a, const dirent **b) @@ -1576,31 +1539,31 @@ int AsciiSort(const dirent **a, const dirent **b) //--------------------------------------------------------------------------- // -/// ファイル再構成 +/// Reconstruct the file /// -/// ここで初めて、ホスト側のファイルシステムの観測が行なわれる。 -/// 必ず上位で排他制御を行なうこと。 +/// Here we carry out the first host side file system observation. +/// Always lock from the top. // //--------------------------------------------------------------------------- void CHostPath::Refresh() { ASSERT(strlen(m_szHost) + 22 < FILEPATH_MAX); - // タイムスタンプ保存 + // Store time stamp Backup(); TCHAR szPath[FILEPATH_MAX]; strcpy(szPath, m_szHost); - // 更新フラグ変更 + // Update refresh flag m_bRefresh = FALSE; - // 以前のキャッシュ内容を保存 + // Store previous cache contents CRing cRingBackup; m_cRing.InsertRing(&cRingBackup); - // ファイル名登録 - /// @todo ファイル重複処理をホスト側APIを経由せずに全て自前で処理する。 + // Register file name + /// TODO: Process file duplication by ourselves rather than using the host API. BOOL bUpdate = FALSE; struct dirent **pd = NULL; int nument = 0; @@ -1616,7 +1579,7 @@ void CHostPath::Refresh() maxent = nument; } - // 最上位ディレクトリならカレントとパレントを対象外とする + // When at the top level directory, exclude current and parent struct dirent* pe = pd[i]; if (m_szHuman[0] == '/' && m_szHuman[1] == 0) { if (strcmp(pe->d_name, ".") == 0 || strcmp(pe->d_name, "..") == 0) { @@ -1624,60 +1587,60 @@ void CHostPath::Refresh() } } - // ファイル名を獲得 + // Get file name strcpy(szFilename, U2S(pe->d_name)); - // ファイル名領域確保 + // Allocate file name memory ring_t* pRing = Alloc(strlen(szFilename)); CHostFilename* pFilename = &pRing->f; pFilename->SetHost(szFilename); - // 以前のキャッシュ内容に該当するファイル名があればそのHuman68k名称を優先する + // If there is a relevant file name in the previous cache, prioritize that for the Human68k name ring_t* pCache = (ring_t*)cRingBackup.Next(); for (;;) { if (pCache == (ring_t*)&cRingBackup) { - pCache = NULL; // 該当するエントリなし - bUpdate = TRUE; // 新規エントリと確定 + pCache = NULL; // No relevant entry + bUpdate = TRUE; // Confirm new entry pFilename->ConvertHuman(); break; } if (strcmp(pFilename->GetHost(), pCache->f.GetHost()) == 0) { - pFilename->CopyHuman(pCache->f.GetHuman()); // Human68k名称のコピー + pFilename->CopyHuman(pCache->f.GetHuman()); // Copy Human68k name break; } pCache = (ring_t*)pCache->r.Next(); } - // 新規エントリの場合はファイル名重複をチェックする - // ホスト側のファイル名から変更があったか、Human68kで表現できないファイル名の場合は - // 以下のチェックを全てパスするファイル名を新たに生成する - // ・正しいファイル名であること - // ・過去のエントリに同名のものが存在しないこと - // ・同名の実ファイル名が存在しないこと - if (pFilename->isReduce() || !pFilename->isCorrect()) { // ファイル名変更が必要か確認 + // If there is a new entry, carry out file name duplication check. + // If the host side file name changed, or if Human68k cannot express the file name, + // generate a new file name that passes all the below checks: + // - File name correctness + // - No duplicated names in previous entries + // - No entity with the same name exists + if (pFilename->isReduce() || !pFilename->isCorrect()) { // Confirm that file name update is required for (DWORD n = 0; n < XM6_HOST_FILENAME_PATTERN_MAX; n++) { - // 正しいファイル名かどうか確認 + // Confirm file name validity if (pFilename->isCorrect()) { - // 過去のエントリと一致するか確認 + // Confirm match with previous entry const CHostFilename* pCheck = FindFilename(pFilename->GetHuman()); if (pCheck == NULL) { - // 一致するものがなければ、実ファイルが存在するか確認 + // If no match, confirm existence of real file strcpy(szPath, m_szHost); - strcat(szPath, (const char*)pFilename->GetHuman()); /// @warning Unicode時要修正 → 済 + strcat(szPath, (const char*)pFilename->GetHuman()); struct stat sb; if (stat(S2U(szPath), &sb)) - break; // 利用可能パターンを発見 + break; // Discover available patterns } } - // 新しい名前を生成 + // Generate new name pFilename->ConvertHuman(n); } } - // ディレクトリエントリ名称 + // Directory entry name pFilename->SetEntryName(); - // 情報取得 + // Get data strcpy(szPath, m_szHost); strcat(szPath, U2S(pe->d_name)); @@ -1685,7 +1648,6 @@ void CHostPath::Refresh() if (stat(S2U(szPath), &sb)) continue; - // 属性 BYTE nHumanAttribute = Human68k::AT_ARCHIVE; if (S_ISDIR(sb.st_mode)) nHumanAttribute = Human68k::AT_DIRECTORY; @@ -1693,11 +1655,9 @@ void CHostPath::Refresh() nHumanAttribute |= Human68k::AT_READONLY; pFilename->SetEntryAttribute(nHumanAttribute); - // サイズ DWORD nHumanSize = (DWORD)sb.st_size; pFilename->SetEntrySize(nHumanSize); - // 日付時刻 WORD nHumanDate = 0; WORD nHumanTime = 0; struct tm* pt = localtime(&sb.st_mtime); @@ -1708,25 +1668,24 @@ void CHostPath::Refresh() pFilename->SetEntryDate(nHumanDate); pFilename->SetEntryTime(nHumanTime); - // クラスタ番号設定 pFilename->SetEntryCluster(0); - // 以前のキャッシュ内容と比較 + // Compare with previous cached contents if (pCache) { if (pCache->f.isSameEntry(pFilename->GetEntry())) { - Free(pRing); // 今回作成したエントリは破棄し - pRing = pCache; // 以前のキャッシュ内容を使う + Free(pRing); // Destroy entry that was created here + pRing = pCache; // Use previous cache } else { - Free(pCache); // 次回の検索対象から除外 - bUpdate = TRUE; // 一致しなければ更新あり + Free(pCache); // Remove from the next search target + bUpdate = TRUE; // Flag for update if no match } } - // リング末尾へ追加 + // Add to end of ring pRing->r.InsertTail(&m_cRing); } - // ディレクトリエントリを解放 + // Release directory entry if (pd) { for (int i = 0; i < nument; i++) { free(pd[i]); @@ -1734,14 +1693,14 @@ void CHostPath::Refresh() free(pd); } - // 残存するキャッシュ内容を削除 + // Delete remaining cache ring_t* p; while ((p = (ring_t*)cRingBackup.Next()) != (ring_t*)&cRingBackup) { - bUpdate = TRUE; // 削除によってエントリ数の減少が判明 + bUpdate = TRUE; // Confirms the decrease in entries due to deletion Free(p); } - // 更新が行なわれたら識別IDを変更 + // Update the identifier if the update has been carried out if (bUpdate) m_nId = ++g_nId; // ASSERT(m_nId); @@ -1749,7 +1708,7 @@ void CHostPath::Refresh() //--------------------------------------------------------------------------- // -/// ホスト側のタイムスタンプを保存 +/// Store the host side time stamp // //--------------------------------------------------------------------------- void CHostPath::Backup() @@ -1762,7 +1721,7 @@ void CHostPath::Backup() size_t len = strlen(szPath); m_tBackup = 0; - if (len > 1) { // ルートディレクトリの場合は何もしない + if (len > 1) { // Don't do anything if it is the root directory len--; ASSERT(szPath[len] == _T('/')); szPath[len] = _T('\0'); @@ -1774,7 +1733,7 @@ void CHostPath::Backup() //--------------------------------------------------------------------------- // -/// ホスト側のタイムスタンプを復元 +/// Restore the host side time stamp // //--------------------------------------------------------------------------- void CHostPath::Restore() const @@ -1801,7 +1760,7 @@ void CHostPath::Restore() const //--------------------------------------------------------------------------- // -/// 更新 +/// Update // //--------------------------------------------------------------------------- void CHostPath::Release() @@ -1812,15 +1771,10 @@ void CHostPath::Release() //=========================================================================== // -// ディレクトリエントリ管理 +// Manage directory entries // //=========================================================================== -//--------------------------------------------------------------------------- -// -/// デフォルトコンストラクタ -// -//--------------------------------------------------------------------------- CHostEntry::CHostEntry() { for (size_t n = 0; n < DriveMax; n++) { @@ -1830,17 +1784,12 @@ CHostEntry::CHostEntry() m_nTimeout = 0; } -//--------------------------------------------------------------------------- -// -/// デストラクタ final -// -//--------------------------------------------------------------------------- CHostEntry::~CHostEntry() { Clean(); #ifdef _DEBUG - // オブジェクト確認 + // Confirm object for (size_t n = 0; n < DriveMax; n++) { ASSERT(m_pDrv[n] == NULL); } @@ -1849,14 +1798,14 @@ CHostEntry::~CHostEntry() //--------------------------------------------------------------------------- // -/// 初期化 (ドライバ組込み時) +/// Initialize (when the driver is installed) // //--------------------------------------------------------------------------- void CHostEntry::Init() { #ifdef _DEBUG - // オブジェクト確認 + // Confirm object for (size_t n = 0; n < DriveMax; n++) { ASSERT(m_pDrv[n] == NULL); } @@ -1865,13 +1814,13 @@ void CHostEntry::Init() //--------------------------------------------------------------------------- // -/// 解放 (起動・リセット時) +/// Release (at startup and reset) // //--------------------------------------------------------------------------- void CHostEntry::Clean() { - // オブジェクト削除 + // Delete object for (size_t n = 0; n < DriveMax; n++) { delete m_pDrv[n]; m_pDrv[n] = NULL; @@ -1880,7 +1829,7 @@ void CHostEntry::Clean() //--------------------------------------------------------------------------- // -/// 全てのキャッシュを更新する +/// Update all cache // //--------------------------------------------------------------------------- void CHostEntry::CleanCache() @@ -1896,7 +1845,7 @@ void CHostEntry::CleanCache() //--------------------------------------------------------------------------- // -/// 指定されたユニットのキャッシュを更新する +/// Update the cache for the specified unit // //--------------------------------------------------------------------------- void CHostEntry::CleanCache(DWORD nUnit) @@ -1909,7 +1858,7 @@ void CHostEntry::CleanCache(DWORD nUnit) //--------------------------------------------------------------------------- // -/// 指定されたパスのキャッシュを更新する +/// Update the cache for the specified path // //--------------------------------------------------------------------------- void CHostEntry::CleanCache(DWORD nUnit, const BYTE* szHumanPath) @@ -1923,7 +1872,7 @@ void CHostEntry::CleanCache(DWORD nUnit, const BYTE* szHumanPath) //--------------------------------------------------------------------------- // -/// 指定されたパス以下のキャッシュを全て更新する +/// Update all cache for the specified path and below // //--------------------------------------------------------------------------- void CHostEntry::CleanCacheChild(DWORD nUnit, const BYTE* szHumanPath) @@ -1937,7 +1886,7 @@ void CHostEntry::CleanCacheChild(DWORD nUnit, const BYTE* szHumanPath) //--------------------------------------------------------------------------- // -/// 指定されたパスのキャッシュを削除する +/// Delete cache for the specified path // //--------------------------------------------------------------------------- void CHostEntry::DeleteCache(DWORD nUnit, const BYTE* szHumanPath) @@ -1951,7 +1900,7 @@ void CHostEntry::DeleteCache(DWORD nUnit, const BYTE* szHumanPath) //--------------------------------------------------------------------------- // -/// ホスト側の名称を検索 (パス名+ファイル名(省略可)+属性) +/// Find host side names (path name + file name (can be abbreviated) + attribute) // //--------------------------------------------------------------------------- BOOL CHostEntry::Find(DWORD nUnit, CHostFiles* pFiles) @@ -1967,7 +1916,7 @@ void CHostEntry::ShellNotify(DWORD, const TCHAR*) {} //--------------------------------------------------------------------------- // -/// ドライブ設定 +/// Drive settings // //--------------------------------------------------------------------------- void CHostEntry::SetDrv(DWORD nUnit, CHostDrv* pDrv) @@ -1980,7 +1929,7 @@ void CHostEntry::SetDrv(DWORD nUnit, CHostDrv* pDrv) //--------------------------------------------------------------------------- // -/// 書き込み禁止か? +/// Is it write-protected? // //--------------------------------------------------------------------------- BOOL CHostEntry::isWriteProtect(DWORD nUnit) const @@ -1993,7 +1942,7 @@ BOOL CHostEntry::isWriteProtect(DWORD nUnit) const //--------------------------------------------------------------------------- // -/// アクセス可能か? +/// Is it accessible? // //--------------------------------------------------------------------------- BOOL CHostEntry::isEnable(DWORD nUnit) const @@ -2006,7 +1955,7 @@ BOOL CHostEntry::isEnable(DWORD nUnit) const //--------------------------------------------------------------------------- // -/// メディアチェック +/// Media check // //--------------------------------------------------------------------------- BOOL CHostEntry::isMediaOffline(DWORD nUnit) @@ -2019,7 +1968,7 @@ BOOL CHostEntry::isMediaOffline(DWORD nUnit) //--------------------------------------------------------------------------- // -/// メディアバイトの取得 +/// Get media byte // //--------------------------------------------------------------------------- BYTE CHostEntry::GetMediaByte(DWORD nUnit) const @@ -2032,7 +1981,7 @@ BYTE CHostEntry::GetMediaByte(DWORD nUnit) const //--------------------------------------------------------------------------- // -/// ドライブ状態の取得 +/// Get drive status // //--------------------------------------------------------------------------- DWORD CHostEntry::GetStatus(DWORD nUnit) const @@ -2045,7 +1994,7 @@ DWORD CHostEntry::GetStatus(DWORD nUnit) const //--------------------------------------------------------------------------- // -/// メディア交換チェック +/// Media change check // //--------------------------------------------------------------------------- BOOL CHostEntry::CheckMedia(DWORD nUnit) @@ -2058,7 +2007,7 @@ BOOL CHostEntry::CheckMedia(DWORD nUnit) //--------------------------------------------------------------------------- // -/// イジェクト +/// Eject // //--------------------------------------------------------------------------- void CHostEntry::Eject(DWORD nUnit) @@ -2071,7 +2020,7 @@ void CHostEntry::Eject(DWORD nUnit) //--------------------------------------------------------------------------- // -/// ボリュームラベルの取得 +/// Get volume label // //--------------------------------------------------------------------------- void CHostEntry::GetVolume(DWORD nUnit, TCHAR* szLabel) @@ -2084,7 +2033,7 @@ void CHostEntry::GetVolume(DWORD nUnit, TCHAR* szLabel) //--------------------------------------------------------------------------- // -/// キャッシュからボリュームラベルを取得 +/// Get volume label from cache // //--------------------------------------------------------------------------- BOOL CHostEntry::GetVolumeCache(DWORD nUnit, TCHAR* szLabel) const @@ -2097,7 +2046,7 @@ BOOL CHostEntry::GetVolumeCache(DWORD nUnit, TCHAR* szLabel) const //--------------------------------------------------------------------------- // -/// 容量の取得 +/// Get capacity // //--------------------------------------------------------------------------- DWORD CHostEntry::GetCapacity(DWORD nUnit, Human68k::capacity_t* pCapacity) @@ -2110,7 +2059,7 @@ DWORD CHostEntry::GetCapacity(DWORD nUnit, Human68k::capacity_t* pCapacity) //--------------------------------------------------------------------------- // -/// キャッシュからクラスタサイズを取得 +/// Get cluster size from cache // //--------------------------------------------------------------------------- BOOL CHostEntry::GetCapacityCache(DWORD nUnit, Human68k::capacity_t* pCapacity) const @@ -2123,13 +2072,13 @@ BOOL CHostEntry::GetCapacityCache(DWORD nUnit, Human68k::capacity_t* pCapacity) //--------------------------------------------------------------------------- // -/// Human68kフルパス名から先頭の要素を分離・コピー +/// Split and copy the first element from the Human68k full path name /// -/// Human68kフルパス名の先頭の要素をパス区切り文字を除外して取得する。 -/// 書き込み先バッファは23バイト必要。 -/// Human68kパスは必ず/で開始すること。 -/// 途中/が2つ以上連続して出現した場合はエラーとする。 -/// 文字列終端が/だけの場合は空の文字列として処理し、エラーにはしない。 +/// Get the first element from the Human68k full path name and delete the path separator char. +/// 23 bytes is required in the buffer to write to. +/// A Human68k path always starts with a '/'. +/// Throw an error if 2 '/' appears in sequence. +/// If the array ends with a '/' treat it as an empty array and don't trow an error. // //--------------------------------------------------------------------------- const BYTE* CHostDrv::SeparateCopyFilename(const BYTE* szHuman, BYTE* szBuffer) // static @@ -2140,60 +2089,55 @@ const BYTE* CHostDrv::SeparateCopyFilename(const BYTE* szHuman, BYTE* szBuffer) const size_t nMax = 22; const BYTE* p = szHuman; - BYTE c = *p++; // 読み込み + BYTE c = *p++; // Read if (c != '/' && c != '\\') - return NULL; // エラー: 不正なパス名 + return NULL; // Error: Invalid path name - // ファイルいっこいれる + // Insert one file size_t i = 0; for (;;) { - c = *p; // 読み込み + c = *p; // Read if (c == '\0') - break; // 文字列終端なら終了 (終端位置を返す) + break; // Exit if at the end of an array (return the end position) if (c == '/' || c == '\\') { if (i == 0) - return NULL; // エラー: パス区切り文字が連続している - break; // パスの区切りを読んだら終了 (文字の位置を返す) + return NULL; // Error: Two separator chars appear in sequence + break; // Exit after reading the separator (return the char position) } p++; if (i >= nMax) - return NULL; // エラー: 1バイト目がバッファ終端にかかる - szBuffer[i++] = c; // 書き込み + return NULL; // Error: The first byte hits the end of the buffer + szBuffer[i++] = c; // Read - if ((0x80 <= c && c <= 0x9F) || 0xE0 <= c) { // 厳密には0x81~0x9Fと0xE0~0xEF - c = *p++; // 読み込み + if ((0x80 <= c && c <= 0x9F) || 0xE0 <= c) { // Specifically 0x81~0x9F and 0xE0~0xEF + c = *p++; // Read if (c < 0x40) - return NULL; // エラー: 不正なSJIS2バイト目 + return NULL; // Error: Invalid Shift-JIS 2nd byte if (i >= nMax) - return NULL; // エラー: 2バイト目がバッファ終端にかかる - szBuffer[i++] = c; // 書き込み + return NULL; // Error: The second byte hits the end of the buffer + szBuffer[i++] = c; // Read } } - szBuffer[i] = '\0'; // 書き込み + szBuffer[i] = '\0'; // Read return p; } //=========================================================================== // -// ファイル検索処理 +// File search processing // //=========================================================================== -//--------------------------------------------------------------------------- -// -/// 初期化 -// -//--------------------------------------------------------------------------- void CHostFiles::Init() { } //--------------------------------------------------------------------------- // -/// パス名・ファイル名を内部で生成 +/// Generate path and file name internally // //--------------------------------------------------------------------------- void CHostFiles::SetPath(const Human68k::namests_t* pNamests) @@ -2209,7 +2153,7 @@ void CHostFiles::SetPath(const Human68k::namests_t* pNamests) //--------------------------------------------------------------------------- // -/// Human68k側でファイルを検索しホスト側の情報を生成 +/// Find file on the Human68k side and create data on the host side // //--------------------------------------------------------------------------- BOOL CHostFiles::Find(DWORD nUnit, CHostEntry* pEntry) @@ -2221,7 +2165,7 @@ BOOL CHostFiles::Find(DWORD nUnit, CHostEntry* pEntry) //--------------------------------------------------------------------------- // -/// ファイル名検索 +/// Find file name // //--------------------------------------------------------------------------- const CHostFilename* CHostFiles::Find(CHostPath* pPath) @@ -2236,23 +2180,23 @@ const CHostFilename* CHostFiles::Find(CHostPath* pPath) //--------------------------------------------------------------------------- // -/// Human68k側の検索結果保存 +/// Store the Human68k side search results // //--------------------------------------------------------------------------- void CHostFiles::SetEntry(const CHostFilename* pFilename) { ASSERT(pFilename); - // Human68kディレクトリエントリ保存 + // Store Human68k directory entry memcpy(&m_dirHuman, pFilename->GetEntry(), sizeof(m_dirHuman)); - // Human68kファイル名保存 + // Stire Human68k file name strcpy((char*)m_szHumanResult, (const char*)pFilename->GetHuman()); } //--------------------------------------------------------------------------- // -/// ホスト側の名称を設定 +/// Set host side name // //--------------------------------------------------------------------------- void CHostFiles::SetResult(const TCHAR* szPath) @@ -2265,7 +2209,7 @@ void CHostFiles::SetResult(const TCHAR* szPath) //--------------------------------------------------------------------------- // -/// ホスト側の名称にファイル名を追加 +/// Add file name to the host side name // //--------------------------------------------------------------------------- void CHostFiles::AddResult(const TCHAR* szPath) @@ -2278,31 +2222,25 @@ void CHostFiles::AddResult(const TCHAR* szPath) //--------------------------------------------------------------------------- // -/// ホスト側の名称にHuman68kの新規ファイル名を追加 +/// Add a new Human68k file name to the host side name // //--------------------------------------------------------------------------- void CHostFiles::AddFilename() { ASSERT(strlen(m_szHostResult) + strlen((const char*)m_szHumanFilename) < FILEPATH_MAX); - /// @warning Unicode未対応。いずれUnicodeの世界に飮まれた時はここで変換を行なう → 済 strncat(m_szHostResult, (const char*)m_szHumanFilename, ARRAY_SIZE(m_szHumanFilename)); } //=========================================================================== // -// ファイル検索領域 マネージャ +// File search memory manager // //=========================================================================== #ifdef _DEBUG -//--------------------------------------------------------------------------- -// -/// デストラクタ final -// -//--------------------------------------------------------------------------- CHostFilesManager::~CHostFilesManager() { - // 実体が存在しないことを確認 (念のため) + // Confirm that the entity does not exist (just in case) ASSERT(m_cRing.Next() == &m_cRing); ASSERT(m_cRing.Prev() == &m_cRing); } @@ -2310,17 +2248,17 @@ CHostFilesManager::~CHostFilesManager() //--------------------------------------------------------------------------- // -/// 初期化 (ドライバ組込み時) +/// Initialization (when the driver is installed) // //--------------------------------------------------------------------------- void CHostFilesManager::Init() { - // 実体が存在しないことを確認 (念のため) + // Confirm that the entity does not exist (just in case) ASSERT(m_cRing.Next() == &m_cRing); ASSERT(m_cRing.Prev() == &m_cRing); - // メモリ確保 + // Allocate memory for (DWORD i = 0; i < XM6_HOST_FILES_MAX; i++) { ring_t* p = new ring_t; ASSERT(p); @@ -2330,54 +2268,43 @@ void CHostFilesManager::Init() //--------------------------------------------------------------------------- // -/// 解放 (起動・リセット時) +/// Release (at startup and reset) // //--------------------------------------------------------------------------- void CHostFilesManager::Clean() { - // メモリ解放 + // Release memory CRing* p; while ((p = m_cRing.Next()) != &m_cRing) { delete (ring_t*)p; } } -//--------------------------------------------------------------------------- -// -/// 確保 -// -//--------------------------------------------------------------------------- CHostFiles* CHostFilesManager::Alloc(DWORD nKey) { ASSERT(nKey); - // 末尾から選択 + // Select from the end ring_t* p = (ring_t*)m_cRing.Prev(); - // リング先頭へ移動 + // Move to the start of the ring p->r.Insert(&m_cRing); - // キーを設定 p->f.SetKey(nKey); return &p->f; } -//--------------------------------------------------------------------------- -// -/// 検索 -// -//--------------------------------------------------------------------------- CHostFiles* CHostFilesManager::Search(DWORD nKey) { - // ASSERT(nKey); // DPB破損により検索キーが0になることもある + // ASSERT(nKey); // The search key may become 0 due to DPB damage - // 該当するオブジェクトを検索 + // Find the relevant object ring_t* p = (ring_t*)m_cRing.Next(); for (; p != (ring_t*)&m_cRing; p = (ring_t*)p->r.Next()) { if (p->f.isSameKey(nKey)) { - // リング先頭へ移動 + // Move to the start of the ring p->r.Insert(&m_cRing); return &p->f; } @@ -2386,35 +2313,25 @@ CHostFiles* CHostFilesManager::Search(DWORD nKey) return NULL; } -//--------------------------------------------------------------------------- -// -/// 解放 -// -//--------------------------------------------------------------------------- void CHostFilesManager::Free(CHostFiles* pFiles) { ASSERT(pFiles); - // 解放 + // Release pFiles->SetKey(0); pFiles->Init(); - // リング末尾へ移動 + // Move to the end of the ring ring_t* p = (ring_t*)((size_t)pFiles - offsetof(ring_t, f)); p->r.InsertTail(&m_cRing); } //=========================================================================== // -// FCB処理 +// FCB processing // //=========================================================================== -//--------------------------------------------------------------------------- -// -/// 初期化 -// -//--------------------------------------------------------------------------- void CHostFcb::Init() { m_bUpdate = FALSE; @@ -2423,7 +2340,7 @@ void CHostFcb::Init() //--------------------------------------------------------------------------- // -/// ファイルオープンモードを設定 +/// Set file open mode // //--------------------------------------------------------------------------- BOOL CHostFcb::SetMode(DWORD nHumanMode) @@ -2447,11 +2364,6 @@ BOOL CHostFcb::SetMode(DWORD nHumanMode) return TRUE; } -//--------------------------------------------------------------------------- -// -/// ファイル名を設定 -// -//--------------------------------------------------------------------------- void CHostFcb::SetFilename(const TCHAR* szFilename) { ASSERT(szFilename); @@ -2460,11 +2372,6 @@ void CHostFcb::SetFilename(const TCHAR* szFilename) strcpy(m_szFilename, szFilename); } -//--------------------------------------------------------------------------- -// -/// Human68kパス名を設定 -// -//--------------------------------------------------------------------------- void CHostFcb::SetHumanPath(const BYTE* szHumanPath) { ASSERT(szHumanPath); @@ -2475,9 +2382,9 @@ void CHostFcb::SetHumanPath(const BYTE* szHumanPath) //--------------------------------------------------------------------------- // -/// ファイル作成 +/// Create file /// -/// エラーの時はFALSEを返す。 +/// Return FALSE if error is thrown. // //--------------------------------------------------------------------------- BOOL CHostFcb::Create(Human68k::fcb_t* pFcb, DWORD nHumanAttribute, BOOL bForce) @@ -2486,24 +2393,24 @@ BOOL CHostFcb::Create(Human68k::fcb_t* pFcb, DWORD nHumanAttribute, BOOL bForce) ASSERT(strlen(m_szFilename) > 0); ASSERT(m_pFile == NULL); - // 重複チェック + // Duplication check if (bForce == FALSE) { struct stat sb; if (stat(S2U(m_szFilename), &sb) == 0) return FALSE; } - // ファイル作成 - m_pFile = fopen(S2U(m_szFilename), "w+b"); /// @warning 理想動作は属性ごと上書き + // Create file + m_pFile = fopen(S2U(m_szFilename), "w+b"); /// @warning The ideal operation is to overwrite each attribute return m_pFile != NULL; } //--------------------------------------------------------------------------- // -/// ファイルオープン +/// File open /// -/// エラーの時はFALSEを返す。 +/// Return FALSE if error is thrown. // //--------------------------------------------------------------------------- BOOL CHostFcb::Open() @@ -2512,14 +2419,14 @@ BOOL CHostFcb::Open() ASSERT(strlen(m_szFilename) > 0); - // ディレクトリなら失敗 + // Fail if directory if (stat(S2U(m_szFilename), &st) == 0) { if ((st.st_mode & S_IFMT) == S_IFDIR) { return FALSE || m_bFlag; } } - // ファイルオープン + // File open if (m_pFile == NULL) m_pFile = fopen(S2U(m_szFilename), m_pszMode); @@ -2528,9 +2435,9 @@ BOOL CHostFcb::Open() //--------------------------------------------------------------------------- // -/// ファイルシーク +/// File seek /// -/// エラーの時はFALSEを返す。 +/// Return FALSE if error is thrown. // //--------------------------------------------------------------------------- BOOL CHostFcb::Rewind(DWORD nOffset) @@ -2545,10 +2452,10 @@ BOOL CHostFcb::Rewind(DWORD nOffset) //--------------------------------------------------------------------------- // -/// ファイル読み込み +/// Read file /// -/// 0バイト読み込みでも正常動作とする。 -/// エラーの時は-1を返す。 +/// Handle a 0 byte read as normal operation too. +/// Return -1 if error is thrown. // //--------------------------------------------------------------------------- DWORD CHostFcb::Read(BYTE* pBuffer, DWORD nSize) @@ -2565,10 +2472,10 @@ DWORD CHostFcb::Read(BYTE* pBuffer, DWORD nSize) //--------------------------------------------------------------------------- // -/// ファイル書き込み +/// Write file /// -/// 0バイト書き込みでも正常動作とする。 -/// エラーの時は-1を返す。 +/// Handle a 0 byte read as normal operation too. +/// Return -1 if error is thrown. // //--------------------------------------------------------------------------- DWORD CHostFcb::Write(const BYTE* pBuffer, DWORD nSize) @@ -2585,9 +2492,9 @@ DWORD CHostFcb::Write(const BYTE* pBuffer, DWORD nSize) //--------------------------------------------------------------------------- // -/// ファイル切り詰め +/// Truncate file /// -/// エラーの時はFALSEを返す。 +/// Return FALSE if error is thrown. // //--------------------------------------------------------------------------- BOOL CHostFcb::Truncate() @@ -2599,9 +2506,9 @@ BOOL CHostFcb::Truncate() //--------------------------------------------------------------------------- // -/// ファイルシーク +/// File seek /// -/// エラーの時は-1を返す。 +/// Return -1 if error is thrown. // //--------------------------------------------------------------------------- DWORD CHostFcb::Seek(DWORD nOffset, DWORD nHumanSeek) @@ -2631,9 +2538,9 @@ DWORD CHostFcb::Seek(DWORD nOffset, DWORD nHumanSeek) //--------------------------------------------------------------------------- // -/// ファイル時刻設定 +/// Set file time stamp /// -/// エラーの時はFALSEを返す。 +/// Return FALSE if error is thrown. // //--------------------------------------------------------------------------- BOOL CHostFcb::TimeStamp(DWORD nHumanTime) @@ -2654,8 +2561,8 @@ BOOL CHostFcb::TimeStamp(DWORD nHumanTime) ut.actime = ti; ut.modtime = ti; - // クローズ時に更新時刻が上書きされるのを防止するため - // タイムスタンプの更新前にフラッシュして同期させる + // This is for preventing the last updated time stamp to be overwritten upon closing. + // Flush and synchronize before updating the time stamp. fflush(m_pFile); return utime(S2U(m_szFilename), &ut) == 0 || m_bFlag; @@ -2663,17 +2570,17 @@ BOOL CHostFcb::TimeStamp(DWORD nHumanTime) //--------------------------------------------------------------------------- // -/// ファイルクローズ +/// File close /// -/// エラーの時はFALSEを返す。 +/// Return FALSE if error is thrown. // //--------------------------------------------------------------------------- BOOL CHostFcb::Close() { BOOL bResult = TRUE; - // ファイルクローズ - // Close→Free(内部で再度Close)という流れもあるので必ず初期化すること。 + // File close + // Always initialize because of the Close→Free (internally one more Close) flow. if (m_pFile) { fclose(m_pFile); m_pFile = NULL; @@ -2689,11 +2596,6 @@ BOOL CHostFcb::Close() //=========================================================================== #ifdef _DEBUG -//--------------------------------------------------------------------------- -// -// Final destructor -// -//--------------------------------------------------------------------------- CHostFcbManager::~CHostFcbManager() { // Confirm that the entity does not exist (just in case) @@ -2737,11 +2639,6 @@ void CHostFcbManager::Clean() } } -//--------------------------------------------------------------------------- -// -// Alloc -// -//--------------------------------------------------------------------------- CHostFcb* CHostFcbManager::Alloc(DWORD nKey) { ASSERT(nKey); @@ -2764,11 +2661,6 @@ CHostFcb* CHostFcbManager::Alloc(DWORD nKey) return &p->f; } -//--------------------------------------------------------------------------- -// -// Search -// -//--------------------------------------------------------------------------- CHostFcb* CHostFcbManager::Search(DWORD nKey) { ASSERT(nKey); @@ -2787,11 +2679,6 @@ CHostFcb* CHostFcbManager::Search(DWORD nKey) return NULL; } -//--------------------------------------------------------------------------- -// -// Free -// -//--------------------------------------------------------------------------- void CHostFcbManager::Free(CHostFcb* pFcb) { ASSERT(pFcb); @@ -2813,11 +2700,6 @@ void CHostFcbManager::Free(CHostFcb* pFcb) DWORD CFileSys::g_nOption; // File name conversion flag -//--------------------------------------------------------------------------- -// -// Default constructor -// -//--------------------------------------------------------------------------- CFileSys::CFileSys() { m_nHostSectorCount = 0; @@ -2830,108 +2712,107 @@ CFileSys::CFileSys() m_szBase[n][0] = _T('\0'); } - // TwentyOneオプション監視初期化 + // Initialize TwentyOne option monitoring m_nKernel = 0; m_nKernelSearch = 0; - // 動作フラグ初期化 + // Initialize operational flags m_nOptionDefault = 0; m_nOption = 0; ASSERT(g_nOption == 0); - // 登録したドライブ数は0 + // Number of registered drives are 0 m_nUnits = 0; } //--------------------------------------------------------------------------- // -/// リセット (全クローズ) +/// Reset (close all) // //--------------------------------------------------------------------------- void CFileSys::Reset() { - // 仮想セクタ領域初期化 + // Initialize virtual sectors m_nHostSectorCount = 0; memset(m_nHostSectorBuffer, 0, sizeof(m_nHostSectorBuffer)); - // ファイル検索領域 解放 (起動・リセット時) + // File search memory - release (on startup and reset) m_cFiles.Clean(); - // FCB操作領域 解放 (起動・リセット時) + // FCB operation memory (on startup and reset) m_cFcb.Clean(); - // ディレクトリエントリ 解放 (起動・リセット時) + // Directory entry - release (on startup and reset) m_cEntry.Clean(); - // TwentyOneオプション監視初期化 + // Initialize TwentyOne option monitoring m_nKernel = 0; m_nKernelSearch = 0; - // 動作フラグ初期化 + // Initialize operational flags SetOption(m_nOptionDefault); } //--------------------------------------------------------------------------- // -/// 初期化 (デバイス起動とロード) +/// Initialize (device startup and load) // //--------------------------------------------------------------------------- void CFileSys::Init() { - // ファイル検索領域 初期化 (デバイス起動・ロード時) + // Initialize file search memory (device startup and load) m_cFiles.Init(); - // FCB操作領域 初期化 (デバイス起動・ロード時) + // Initialize FCB operation memory (device startup and load) m_cFcb.Init(); - // ディレクトリエントリ 初期化 (デバイス起動・ロード時) + // Initialize directory entries (device startup and load) m_cEntry.Init(); - // パス個別設定の有無を判定 + // Evaluate per-path setting validity DWORD nDrives = m_nDrives; if (nDrives == 0) { - // 個別設定を使わずにルートディレクトリを使用する + // Use root directory instead of per-path settings strcpy(m_szBase[0], _T("/")); m_nFlag[0] = 0; nDrives++; } - // ファイルシステムを登録 + // Register file system DWORD nUnit = 0; for (DWORD n = 0; n < nDrives; n++) { - // ベースパスが存在しないエントリは登録しない + // Don't register is base path do not exist if (m_szBase[n][0] == _T('\0')) continue; - // ファイルシステムを1ユニット生成 + // Create 1 unit file system CHostDrv* p = new CHostDrv; // std::nothrow if (p) { m_cEntry.SetDrv(nUnit, p); p->Init(m_szBase[n], m_nFlag[n]); - // 次のユニットへ + // To the next unit nUnit++; } } - // 登録したドライブ数を保存 + // Store the registered number of drives m_nUnits = nUnit; } //--------------------------------------------------------------------------- // -/// $40 - デバイス起動 +/// $40 - Device startup // //--------------------------------------------------------------------------- DWORD CFileSys::InitDevice(const Human68k::argument_t* pArgument) { - // オプション初期化 InitOption(pArgument); - // ファイルシステム初期化 + // File system initialization Init(); return m_nUnits; @@ -2939,20 +2820,20 @@ DWORD CFileSys::InitDevice(const Human68k::argument_t* pArgument) //--------------------------------------------------------------------------- // -/// $41 - ディレクトリチェック +/// $41 - Directory check // //--------------------------------------------------------------------------- int CFileSys::CheckDir(DWORD nUnit, const Human68k::namests_t* pNamests) { ASSERT(pNamests); - // ユニットチェック + // Unit check if (nUnit >= DriveMax) return FS_FATAL_INVALIDUNIT; if (nUnit >= m_nUnits) - return FS_INVALIDFUNC; // レジューム後に無効なドライブでmint操作時に白帯を出さないよう改良 + return FS_INVALIDFUNC; // Avoid triggering a fatal error in mint when resuming with an invalid drive - // パス名生成 + // Generate path name CHostFiles f; f.SetPath(pNamests); if (f.isRootPath()) @@ -2966,29 +2847,29 @@ int CFileSys::CheckDir(DWORD nUnit, const Human68k::namests_t* pNamests) //--------------------------------------------------------------------------- // -/// $42 - ディレクトリ作成 +/// $42 - Create directory // //--------------------------------------------------------------------------- int CFileSys::MakeDir(DWORD nUnit, const Human68k::namests_t* pNamests) { ASSERT(pNamests); - // ユニットチェック + // Unit check if (nUnit >= DriveMax) return FS_FATAL_INVALIDUNIT; ASSERT(nUnit < m_nUnits); if (nUnit >= m_nUnits) - return FS_FATAL_MEDIAOFFLINE; // 念のため + return FS_FATAL_MEDIAOFFLINE; // Just in case - // メディアチェック + // Media check if (m_cEntry.isMediaOffline(nUnit)) return FS_FATAL_MEDIAOFFLINE; - // 書き込み禁止チェック + // Write-protect check if (m_cEntry.isWriteProtect(nUnit)) return FS_FATAL_WRITEPROTECT; - // パス名生成 + // Generate path name CHostFiles f; f.SetPath(pNamests); f.SetPathOnly(); @@ -2996,11 +2877,11 @@ int CFileSys::MakeDir(DWORD nUnit, const Human68k::namests_t* pNamests) return FS_INVALIDPATH; f.AddFilename(); - // ディレクトリ作成 + // Create directory if (mkdir(S2U(f.GetPath()), 0777)) return FS_INVALIDPATH; - // キャッシュ更新 + // Update cache m_cEntry.CleanCache(nUnit, f.GetHumanPath()); return 0; @@ -3008,36 +2889,36 @@ int CFileSys::MakeDir(DWORD nUnit, const Human68k::namests_t* pNamests) //--------------------------------------------------------------------------- // -/// $43 - ディレクトリ削除 +/// $43 - Delete directory // //--------------------------------------------------------------------------- int CFileSys::RemoveDir(DWORD nUnit, const Human68k::namests_t* pNamests) { ASSERT(pNamests); - // ユニットチェック + // Unit check if (nUnit >= DriveMax) return FS_FATAL_INVALIDUNIT; ASSERT(nUnit < m_nUnits); if (nUnit >= m_nUnits) - return FS_FATAL_MEDIAOFFLINE; // 念のため + return FS_FATAL_MEDIAOFFLINE; // Just in case - // メディアチェック + // Media check if (m_cEntry.isMediaOffline(nUnit)) return FS_FATAL_MEDIAOFFLINE; - // 書き込み禁止チェック + // Write-protect check if (m_cEntry.isWriteProtect(nUnit)) return FS_FATAL_WRITEPROTECT; - // パス名生成 + // Generate path name CHostFiles f; f.SetPath(pNamests); f.SetAttribute(Human68k::AT_DIRECTORY); if (f.Find(nUnit, &m_cEntry) == FALSE) return FS_DIRNOTFND; - // キャッシュ削除 + // Delete cache BYTE szHuman[HUMAN68K_PATH_MAX + 24]; ASSERT(strlen((const char*)f.GetHumanPath()) + strlen((const char*)f.GetHumanFilename()) < HUMAN68K_PATH_MAX + 24); @@ -3046,11 +2927,11 @@ int CFileSys::RemoveDir(DWORD nUnit, const Human68k::namests_t* pNamests) strcat((char*)szHuman, "/"); m_cEntry.DeleteCache(nUnit, szHuman); - // ディレクトリ削除 + // Delete directory if (rmdir(S2U(f.GetPath()))) return FS_CANTDELETE; - // キャッシュ更新 + // Update cache m_cEntry.CleanCache(nUnit, f.GetHumanPath()); return 0; @@ -3058,29 +2939,29 @@ int CFileSys::RemoveDir(DWORD nUnit, const Human68k::namests_t* pNamests) //--------------------------------------------------------------------------- // -/// $44 - ファイル名変更 +/// $44 - Change file name // //--------------------------------------------------------------------------- int CFileSys::Rename(DWORD nUnit, const Human68k::namests_t* pNamests, const Human68k::namests_t* pNamestsNew) { ASSERT(pNamests); - // ユニットチェック + // Unit check if (nUnit >= DriveMax) return FS_FATAL_INVALIDUNIT; ASSERT(nUnit < m_nUnits); if (nUnit >= m_nUnits) - return FS_FATAL_MEDIAOFFLINE; // 念のため + return FS_FATAL_MEDIAOFFLINE; // Just in case - // メディアチェック + // Media check if (m_cEntry.isMediaOffline(nUnit)) return FS_FATAL_MEDIAOFFLINE; - // 書き込み禁止チェック + // Write-protect check if (m_cEntry.isWriteProtect(nUnit)) return FS_FATAL_WRITEPROTECT; - // パス名生成 + // Generate path name CHostFiles f; f.SetPath(pNamests); f.SetAttribute(Human68k::AT_ALL); @@ -3094,11 +2975,11 @@ int CFileSys::Rename(DWORD nUnit, const Human68k::namests_t* pNamests, const Hum return FS_INVALIDPATH; fNew.AddFilename(); - // キャッシュ更新 + // Update cache if (f.GetAttribute() & Human68k::AT_DIRECTORY) m_cEntry.CleanCacheChild(nUnit, f.GetHumanPath()); - // ファイル名変更 + // Change file name char szFrom[FILENAME_MAX]; char szTo[FILENAME_MAX]; SJIS2UTF8(f.GetPath(), szFrom, FILENAME_MAX); @@ -3107,7 +2988,7 @@ int CFileSys::Rename(DWORD nUnit, const Human68k::namests_t* pNamests, const Hum return FS_FILENOTFND; } - // キャッシュ更新 + // Update cache m_cEntry.CleanCache(nUnit, f.GetHumanPath()); m_cEntry.CleanCache(nUnit, fNew.GetHumanPath()); @@ -3116,39 +2997,39 @@ int CFileSys::Rename(DWORD nUnit, const Human68k::namests_t* pNamests, const Hum //--------------------------------------------------------------------------- // -/// $45 - ファイル削除 +/// $45 - Delete file // //--------------------------------------------------------------------------- int CFileSys::Delete(DWORD nUnit, const Human68k::namests_t* pNamests) { ASSERT(pNamests); - // ユニットチェック + // Unit check if (nUnit >= DriveMax) return FS_FATAL_INVALIDUNIT; ASSERT(nUnit < m_nUnits); if (nUnit >= m_nUnits) - return FS_FATAL_MEDIAOFFLINE; // 念のため + return FS_FATAL_MEDIAOFFLINE; // Just in case - // メディアチェック + // Media check if (m_cEntry.isMediaOffline(nUnit)) return FS_FATAL_MEDIAOFFLINE; - // 書き込み禁止チェック + // Write-protect check if (m_cEntry.isWriteProtect(nUnit)) return FS_FATAL_WRITEPROTECT; - // パス名生成 + // Generate path name CHostFiles f; f.SetPath(pNamests); if (f.Find(nUnit, &m_cEntry) == FALSE) return FS_FILENOTFND; - // ファイル削除 + // Delete file if (unlink(S2U(f.GetPath()))) return FS_CANTDELETE; - // キャッシュ更新 + // Update cache m_cEntry.CleanCache(nUnit, f.GetHumanPath()); return 0; @@ -3156,43 +3037,43 @@ int CFileSys::Delete(DWORD nUnit, const Human68k::namests_t* pNamests) //--------------------------------------------------------------------------- // -/// $46 - ファイル属性取得/設定 +/// $46 - Get/set file attribute // //--------------------------------------------------------------------------- int CFileSys::Attribute(DWORD nUnit, const Human68k::namests_t* pNamests, DWORD nHumanAttribute) { ASSERT(pNamests); - // ユニットチェック + // Unit check if (nUnit >= DriveMax) return FS_FATAL_INVALIDUNIT; if (nUnit >= m_nUnits) - return FS_FATAL_MEDIAOFFLINE; // レジューム後に無効なドライブ上で発生 + return FS_FATAL_MEDIAOFFLINE; // This occurs when resuming with an invalid drive - // メディアチェック + // Media check if (m_cEntry.isMediaOffline(nUnit)) return FS_FATAL_MEDIAOFFLINE; - // パス名生成 + // Generate path name CHostFiles f; f.SetPath(pNamests); f.SetAttribute(Human68k::AT_ALL); if (f.Find(nUnit, &m_cEntry) == FALSE) return FS_FILENOTFND; - // 属性取得なら終了 + // Exit if attribute is acquired if (nHumanAttribute == 0xFF) return f.GetAttribute(); - // 属性チェック + // Attribute check if (nHumanAttribute & Human68k::AT_VOLUME) return FS_CANTACCESS; - // 書き込み禁止チェック + // Write-protect check if (m_cEntry.isWriteProtect(nUnit)) return FS_FATAL_WRITEPROTECT; - // 属性生成 + // Generate attribute DWORD nAttribute = (nHumanAttribute & Human68k::AT_READONLY) | (f.GetAttribute() & ~Human68k::AT_READONLY); if (f.GetAttribute() != nAttribute) { @@ -3205,15 +3086,15 @@ int CFileSys::Attribute(DWORD nUnit, const Human68k::namests_t* pNamests, DWORD else m |= 0200; // u+w - // 属性設定 + // Set attribute if (chmod(S2U(f.GetPath()), m)) return FS_FILENOTFND; } - // キャッシュ更新 + // Update cache m_cEntry.CleanCache(nUnit, f.GetHumanPath()); - // 変更後の属性取得 + // Get attribute after changing if (f.Find(nUnit, &m_cEntry) == FALSE) return FS_FILENOTFND; @@ -3222,7 +3103,7 @@ int CFileSys::Attribute(DWORD nUnit, const Human68k::namests_t* pNamests, DWORD //--------------------------------------------------------------------------- // -/// $47 - ファイル検索 +/// $47 - File search // //--------------------------------------------------------------------------- int CFileSys::Files(DWORD nUnit, DWORD nKey, const Human68k::namests_t* pNamests, Human68k::files_t* pFiles) @@ -3231,49 +3112,48 @@ int CFileSys::Files(DWORD nUnit, DWORD nKey, const Human68k::namests_t* pNamests ASSERT(nKey); ASSERT(pFiles); - // 既に同じキーを持つ領域があれば解放しておく + // Release if memory with the same key already exists CHostFiles* pHostFiles = m_cFiles.Search(nKey); if (pHostFiles != NULL) { m_cFiles.Free(pHostFiles); } - // ユニットチェック + // Unit check if (nUnit >= DriveMax) return FS_FATAL_INVALIDUNIT; if (nUnit >= m_nUnits) - return FS_FATAL_MEDIAOFFLINE; // レジューム後に無効なドライブ上で発生 + return FS_FATAL_MEDIAOFFLINE; // This occurs when resuming with an invalid drive - // ボリュームラベルの取得 + // Get volume label /** @note - 直前のメディア交換チェックで正しくエラーを返しているにもかかわら - ず、ボリュームラベルの取得を実行する行儀の悪いアプリでホスト側の - リムーバブルメディア(CD-ROMドライブ等)が白帯を出すのを防ぐため、 - ボリュームラベルの取得はメディアチェックをせずに行なう仕様とした。 + This skips the media check when getting the volume label to avoid triggering a fatal error + with host side removable media (CD-ROM, etc.) Some poorly coded applications will attempt to + get the volume label even though a proper error was thrown doing a media change check just before. */ if ((pFiles->fatr & (Human68k::AT_ARCHIVE | Human68k::AT_DIRECTORY | Human68k::AT_VOLUME)) == Human68k::AT_VOLUME) { - // パスチェック + // Path check CHostFiles f; f.SetPath(pNamests); if (f.isRootPath() == FALSE) return FS_FILENOTFND; - // バッファを確保せず、いきなり結果を返す + // Immediately return the results without allocating buffer if (FilesVolume(nUnit, pFiles) == FALSE) return FS_FILENOTFND; return 0; } - // メディアチェック + // Media check if (m_cEntry.isMediaOffline(nUnit)) return FS_FATAL_MEDIAOFFLINE; - // バッファ確保 + // Allocate buffer pHostFiles = m_cFiles.Alloc(nKey); if (pHostFiles == NULL) return FS_OUTOFMEM; - // ディレクトリチェック + // Directory check pHostFiles->SetPath(pNamests); if (pHostFiles->isRootPath() == FALSE) { pHostFiles->SetPathOnly(); @@ -3283,30 +3163,30 @@ int CFileSys::Files(DWORD nUnit, DWORD nKey, const Human68k::namests_t* pNamests } } - // ワイルドカード使用可能に設定 + // Enable wildcards pHostFiles->SetPathWildcard(); pHostFiles->SetAttribute(pFiles->fatr); - // ファイル検索 + // Find file if (pHostFiles->Find(nUnit, &m_cEntry) == FALSE) { m_cFiles.Free(pHostFiles); return FS_FILENOTFND; } - // 検索結果を格納 + // Store search results pFiles->attr = (BYTE)pHostFiles->GetAttribute(); pFiles->date = pHostFiles->GetDate(); pFiles->time = pHostFiles->GetTime(); pFiles->size = pHostFiles->GetSize(); strcpy((char*)pFiles->full, (const char*)pHostFiles->GetHumanResult()); - // 擬似ディレクトリエントリを指定 + // Specify pseudo-directory entry pFiles->sector = nKey; pFiles->offset = 0; - // ファイル名にワイルドカードがなければ、この時点でバッファを解放可能 + // When the file name does not include wildcards, the buffer may be released if (pNamests->wildcard == 0) { - // しかし、仮想セクタのエミュレーションで使う可能性があるため、すぐには解放しない + // However, there is a chance the virtual selector may be used for emulation, so don't release immediately // m_cFiles.Free(pHostFiles); } @@ -3315,7 +3195,7 @@ int CFileSys::Files(DWORD nUnit, DWORD nKey, const Human68k::namests_t* pNamests //--------------------------------------------------------------------------- // -/// $48 - ファイル次検索 +/// $48 - Search next file // //--------------------------------------------------------------------------- int CFileSys::NFiles(DWORD nUnit, DWORD nKey, Human68k::files_t* pFiles) @@ -3323,22 +3203,22 @@ int CFileSys::NFiles(DWORD nUnit, DWORD nKey, Human68k::files_t* pFiles) ASSERT(nKey); ASSERT(pFiles); - // ユニットチェック + // Unit check if (nUnit >= DriveMax) return FS_FATAL_INVALIDUNIT; if (nUnit >= m_nUnits) - return FS_FATAL_MEDIAOFFLINE; // レジューム後に無効なドライブ上で発生 + return FS_FATAL_MEDIAOFFLINE; // This occurs when resuming with an invalid drive - // メディアチェック + // Media check if (m_cEntry.isMediaOffline(nUnit)) return FS_FATAL_MEDIAOFFLINE; - // バッファ検索 + // Find buffer CHostFiles* pHostFiles = m_cFiles.Search(nKey); if (pHostFiles == NULL) return FS_INVALIDPTR; - // ファイル検索 + // Find file if (pHostFiles->Find(nUnit, &m_cEntry) == FALSE) { m_cFiles.Free(pHostFiles); return FS_FILENOTFND; @@ -3347,7 +3227,7 @@ int CFileSys::NFiles(DWORD nUnit, DWORD nKey, Human68k::files_t* pFiles) ASSERT(pFiles->sector == nKey); ASSERT(pFiles->offset == 0); - // 検索結果を格納 + // Store search results pFiles->attr = (BYTE)pHostFiles->GetAttribute(); pFiles->date = pHostFiles->GetDate(); pFiles->time = pHostFiles->GetTime(); @@ -3359,7 +3239,7 @@ int CFileSys::NFiles(DWORD nUnit, DWORD nKey, Human68k::files_t* pFiles) //--------------------------------------------------------------------------- // -/// $49 - ファイル新規作成 +/// $49 - Create new file // //--------------------------------------------------------------------------- int CFileSys::Create(DWORD nUnit, DWORD nKey, const Human68k::namests_t* pNamests, Human68k::fcb_t* pFcb, DWORD nHumanAttribute, BOOL bForce) @@ -3368,26 +3248,26 @@ int CFileSys::Create(DWORD nUnit, DWORD nKey, const Human68k::namests_t* pNamest ASSERT(nKey); ASSERT(pFcb); - // ユニットチェック + // Unit check if (nUnit >= DriveMax) return FS_FATAL_INVALIDUNIT; ASSERT(nUnit < m_nUnits); if (nUnit >= m_nUnits) - return FS_FATAL_MEDIAOFFLINE; // 念のため + return FS_FATAL_MEDIAOFFLINE; // Just in case - // メディアチェック + // Media check if (m_cEntry.isMediaOffline(nUnit)) return FS_FATAL_MEDIAOFFLINE; - // 書き込み禁止チェック + // Write-protect check if (m_cEntry.isWriteProtect(nUnit)) return FS_FATAL_WRITEPROTECT; - // 既に同じキーを持つ領域があればエラーとする + // Release if memory with the same key already exists if (m_cFcb.Search(nKey) != NULL) return FS_INVALIDPTR; - // パス名生成 + // Generate path name CHostFiles f; f.SetPath(pNamests); f.SetPathOnly(); @@ -3395,31 +3275,31 @@ int CFileSys::Create(DWORD nUnit, DWORD nKey, const Human68k::namests_t* pNamest return FS_INVALIDPATH; f.AddFilename(); - // 属性チェック + // Attribute check if (nHumanAttribute & (Human68k::AT_DIRECTORY | Human68k::AT_VOLUME)) return FS_CANTACCESS; - // パス名保存 + // Store path name CHostFcb* pHostFcb = m_cFcb.Alloc(nKey); if (pHostFcb == NULL) return FS_OUTOFMEM; pHostFcb->SetFilename(f.GetPath()); pHostFcb->SetHumanPath(f.GetHumanPath()); - // オープンモード設定 + // Set open mode pFcb->mode = (WORD)((pFcb->mode & ~Human68k::OP_MASK) | Human68k::OP_FULL); if (pHostFcb->SetMode(pFcb->mode) == FALSE) { m_cFcb.Free(pHostFcb); return FS_ILLEGALMOD; } - // ファイル作成 + // Create file if (pHostFcb->Create(pFcb, nHumanAttribute, bForce) == FALSE) { m_cFcb.Free(pHostFcb); return FS_FILEEXIST; } - // キャッシュ更新 + // Update cache m_cEntry.CleanCache(nUnit, f.GetHumanPath()); return 0; @@ -3427,7 +3307,7 @@ int CFileSys::Create(DWORD nUnit, DWORD nKey, const Human68k::namests_t* pNamest //--------------------------------------------------------------------------- // -/// $4A - ファイルオープン +/// $4A - File open // //--------------------------------------------------------------------------- int CFileSys::Open(DWORD nUnit, DWORD nKey, const Human68k::namests_t* pNamests, Human68k::fcb_t* pFcb) @@ -3436,17 +3316,17 @@ int CFileSys::Open(DWORD nUnit, DWORD nKey, const Human68k::namests_t* pNamests, ASSERT(nKey); ASSERT(pFcb); - // ユニットチェック + // Unit check if (nUnit >= DriveMax) return FS_FATAL_INVALIDUNIT; if (nUnit >= m_nUnits) - return FS_FATAL_MEDIAOFFLINE; // レジューム後に無効なドライブ上で発生 + return FS_FATAL_MEDIAOFFLINE; // This occurs when resuming with an invalid drive - // メディアチェック + // Media check if (m_cEntry.isMediaOffline(nUnit)) return FS_FATAL_MEDIAOFFLINE; - // 書き込み禁止チェック + // Write-protect check switch (pFcb->mode & Human68k::OP_MASK) { case Human68k::OP_WRITE: case Human68k::OP_FULL: @@ -3454,38 +3334,38 @@ int CFileSys::Open(DWORD nUnit, DWORD nKey, const Human68k::namests_t* pNamests, return FS_FATAL_WRITEPROTECT; } - // 既に同じキーを持つ領域があればエラーとする + // Release if memory with the same key already exists if (m_cFcb.Search(nKey) != NULL) return FS_INVALIDPRM; - // パス名生成 + // Generate path name CHostFiles f; f.SetPath(pNamests); f.SetAttribute(Human68k::AT_ALL); if (f.Find(nUnit, &m_cEntry) == FALSE) return FS_FILENOTFND; - // タイムスタンプ + // Time stamp pFcb->date = f.GetDate(); pFcb->time = f.GetTime(); - // ファイルサイズ + // File size pFcb->size = f.GetSize(); - // パス名保存 + // Store path name CHostFcb* pHostFcb = m_cFcb.Alloc(nKey); if (pHostFcb == NULL) return FS_OUTOFMEM; pHostFcb->SetFilename(f.GetPath()); pHostFcb->SetHumanPath(f.GetHumanPath()); - // オープンモード設定 + // Set open mode if (pHostFcb->SetMode(pFcb->mode) == FALSE) { m_cFcb.Free(pHostFcb); return FS_ILLEGALMOD; } - // ファイルオープン + // File open if (pHostFcb->Open() == FALSE) { m_cFcb.Free(pHostFcb); return FS_INVALIDPATH; @@ -3496,135 +3376,135 @@ int CFileSys::Open(DWORD nUnit, DWORD nKey, const Human68k::namests_t* pNamests, //--------------------------------------------------------------------------- // -/// $4B - ファイルクローズ +/// $4B - File close // //--------------------------------------------------------------------------- int CFileSys::Close(DWORD nUnit, DWORD nKey, Human68k::fcb_t* /* pFcb */) { ASSERT(nKey); - // ユニットチェック + // Unit check if (nUnit >= DriveMax) return FS_FATAL_INVALIDUNIT; if (nUnit >= m_nUnits) - return FS_FATAL_MEDIAOFFLINE; // レジューム後に無効なドライブ上で発生 + return FS_FATAL_MEDIAOFFLINE; // This occurs when resuming with an invalid drive - // メディアチェック + // Media check if (m_cEntry.isMediaOffline(nUnit)) return FS_FATAL_MEDIAOFFLINE; - // 既に同じキーを持つ領域がなければエラーとする + // Throw error if memory with the same key does not exist CHostFcb* pHostFcb = m_cFcb.Search(nKey); if (pHostFcb == NULL) return FS_INVALIDPRM; - // ファイルクローズと領域解放 + // File close and release memory m_cFcb.Free(pHostFcb); - // キャッシュ更新 + // Update cache if (pHostFcb->isUpdate()) m_cEntry.CleanCache(nUnit, pHostFcb->GetHumanPath()); - /// @note クローズ時のFCBの状態を他のデバイスと合わせたい + /// TODO: Match the FCB status on close with other devices return 0; } //--------------------------------------------------------------------------- // -/// $4C - ファイル読み込み +/// $4C - Read file /// -/// 0バイト読み込みでも正常終了する。 +/// Clean exit when 0 bytes are read. // //--------------------------------------------------------------------------- int CFileSys::Read(DWORD nKey, Human68k::fcb_t* pFcb, BYTE* pBuffer, DWORD nSize) { ASSERT(nKey); ASSERT(pFcb); - // ASSERT(pBuffer); // 必要時のみ判定 - ASSERT(nSize <= 0xFFFFFF); // クリップ済 + // ASSERT(pBuffer); // Evaluate only when needed + ASSERT(nSize <= 0xFFFFFF); // Clipped - // 既に同じキーを持つ領域がなければエラーとする + // Throw error if memory with the same key does not exist CHostFcb* pHostFcb = m_cFcb.Search(nKey); if (pHostFcb == NULL) return FS_NOTOPENED; - // バッファ存在確認 + // Confirm the existence of the buffer if (pBuffer == NULL) { m_cFcb.Free(pHostFcb); return FS_INVALIDFUNC; } - // 読み込み + // Read DWORD nResult; nResult = pHostFcb->Read(pBuffer, nSize); if (nResult == (DWORD)-1) { m_cFcb.Free(pHostFcb); - return FS_INVALIDFUNC; /// @note これに加えてエラーコード10(読み込みエラー)を返すべき + return FS_INVALIDFUNC; // TODO: Should return error code 10 (read error) as well here } ASSERT(nResult <= nSize); - // ファイルポインタ更新 - pFcb->fileptr += nResult; /// @note オーバーフロー確認は必要じゃろうか? + // Update file pointer + pFcb->fileptr += nResult; /// TODO: Maybe an overflow check is needed here? return nResult; } //--------------------------------------------------------------------------- // -/// $4D - ファイル書き込み +/// $4D - Write file /// -/// 0バイト書き込みの場合はファイルを切り詰める。 +/// Truncate file if 0 bytes are written. // //--------------------------------------------------------------------------- int CFileSys::Write(DWORD nKey, Human68k::fcb_t* pFcb, const BYTE* pBuffer, DWORD nSize) { ASSERT(nKey); ASSERT(pFcb); - // ASSERT(pBuffer); // 必要時のみ判定 - ASSERT(nSize <= 0xFFFFFF); // クリップ済 + // ASSERT(pBuffer); // Evaluate only when needed + ASSERT(nSize <= 0xFFFFFF); // Clipped - // 既に同じキーを持つ領域がなければエラーとする + // Throw error if memory with the same key does not exist CHostFcb* pHostFcb = m_cFcb.Search(nKey); if (pHostFcb == NULL) return FS_NOTOPENED; DWORD nResult; if (nSize == 0) { - // 切り詰め + // Truncate if (pHostFcb->Truncate() == FALSE) { m_cFcb.Free(pHostFcb); return FS_CANTSEEK; } - // ファイルサイズ更新 + // Update file size pFcb->size = pFcb->fileptr; nResult = 0; } else { - // バッファ存在確認 + // Confirm the existence of the buffer if (pBuffer == NULL) { m_cFcb.Free(pHostFcb); return FS_INVALIDFUNC; } - // 書き込み + // Write nResult = pHostFcb->Write(pBuffer, nSize); if (nResult == (DWORD)-1) { m_cFcb.Free(pHostFcb); - return FS_CANTWRITE; /// @note これに加えてエラーコード11(書き込みエラー)を返すべき + return FS_CANTWRITE; /// TODO: Should return error code 11 (write error) as well here } ASSERT(nResult <= nSize); - // ファイルポインタ更新 - pFcb->fileptr += nResult; /// @note オーバーフロー確認は必要じゃろうか? + // Update file pointer + pFcb->fileptr += nResult; /// TODO: Do we need an overflow check here? - // ファイルサイズ更新 + // Update file size if (pFcb->size < pFcb->fileptr) pFcb->size = pFcb->fileptr; } - // フラグ更新 + // Update flag pHostFcb->SetUpdate(); return nResult; @@ -3632,32 +3512,32 @@ int CFileSys::Write(DWORD nKey, Human68k::fcb_t* pFcb, const BYTE* pBuffer, DWOR //--------------------------------------------------------------------------- // -/// $4E - ファイルシーク +/// $4E - File seek // //--------------------------------------------------------------------------- int CFileSys::Seek(DWORD nKey, Human68k::fcb_t* pFcb, DWORD nSeek, int nOffset) { ASSERT(pFcb); - // 既に同じキーを持つ領域がなければエラーとする + // Throw error if memory with the same key does not exist CHostFcb* pHostFcb = m_cFcb.Search(nKey); if (pHostFcb == NULL) return FS_NOTOPENED; - // パラメータチェック + // Parameter check if (nSeek > Human68k::SK_END) { m_cFcb.Free(pHostFcb); return FS_INVALIDPRM; } - // ファイルシーク + // File seek DWORD nResult = pHostFcb->Seek(nOffset, nSeek); if (nResult == (DWORD)-1) { m_cFcb.Free(pHostFcb); return FS_CANTSEEK; } - // ファイルポインタ更新 + // Update file pointer pFcb->fileptr = nResult; return nResult; @@ -3665,9 +3545,9 @@ int CFileSys::Seek(DWORD nKey, Human68k::fcb_t* pFcb, DWORD nSeek, int nOffset) //--------------------------------------------------------------------------- // -/// $4F - ファイル時刻取得/設定 +/// $4F - Get/set file time stamp /// -/// 結果の上位16Bitが$FFFFだとエラー。 +/// Throw error when the top 16 bits are $FFFF. // //--------------------------------------------------------------------------- DWORD CFileSys::TimeStamp(DWORD nUnit, DWORD nKey, Human68k::fcb_t* pFcb, DWORD nHumanTime) @@ -3675,31 +3555,31 @@ DWORD CFileSys::TimeStamp(DWORD nUnit, DWORD nKey, Human68k::fcb_t* pFcb, DWORD ASSERT(nKey); ASSERT(pFcb); - // 取得のみ + // Get only if (nHumanTime == 0) return ((DWORD)pFcb->date << 16) | pFcb->time; - // ユニットチェック + // Unit check if (nUnit >= DriveMax) return FS_FATAL_INVALIDUNIT; ASSERT(nUnit < m_nUnits); if (nUnit >= m_nUnits) - return FS_FATAL_MEDIAOFFLINE; // 念のため + return FS_FATAL_MEDIAOFFLINE; // Just in case - // メディアチェック + // Media check if (m_cEntry.isMediaOffline(nUnit)) return FS_FATAL_MEDIAOFFLINE; - // 書き込み禁止チェック + // Write-protect check if (m_cEntry.isWriteProtect(nUnit)) return FS_FATAL_WRITEPROTECT; - // 既に同じキーを持つ領域がなければエラーとする + // Throw error if memory with the same key does not exist CHostFcb* pHostFcb = m_cFcb.Search(nKey); if (pHostFcb == NULL) return FS_NOTOPENED; - // 時刻設定 + // Set time stamp if (pHostFcb->TimeStamp(nHumanTime) == FALSE) { m_cFcb.Free(pHostFcb); return FS_INVALIDPRM; @@ -3707,7 +3587,7 @@ DWORD CFileSys::TimeStamp(DWORD nUnit, DWORD nKey, Human68k::fcb_t* pFcb, DWORD pFcb->date = (WORD)(nHumanTime >> 16); pFcb->time = (WORD)nHumanTime; - // キャッシュ更新 + // Update cache m_cEntry.CleanCache(nUnit, pHostFcb->GetHumanPath()); return 0; @@ -3715,59 +3595,59 @@ DWORD CFileSys::TimeStamp(DWORD nUnit, DWORD nKey, Human68k::fcb_t* pFcb, DWORD //--------------------------------------------------------------------------- // -/// $50 - 容量取得 +/// $50 - Get capacity // //--------------------------------------------------------------------------- int CFileSys::GetCapacity(DWORD nUnit, Human68k::capacity_t* pCapacity) { ASSERT(pCapacity); - // ユニットチェック + // Unit check if (nUnit >= DriveMax) return FS_FATAL_INVALIDUNIT; ASSERT(nUnit < m_nUnits); if (nUnit >= m_nUnits) - return FS_FATAL_MEDIAOFFLINE; // 念のため + return FS_FATAL_MEDIAOFFLINE; // Just in case - // メディアチェック + // Media check if (m_cEntry.isMediaOffline(nUnit)) return FS_FATAL_MEDIAOFFLINE; - // 容量取得 + // Get capacity return m_cEntry.GetCapacity(nUnit, pCapacity); } //--------------------------------------------------------------------------- // -/// $51 - ドライブ状態検査/制御 +/// $51 - Inspect/control drive status // //--------------------------------------------------------------------------- int CFileSys::CtrlDrive(DWORD nUnit, Human68k::ctrldrive_t* pCtrlDrive) { ASSERT(pCtrlDrive); - // ユニットチェック + // Unit check if (nUnit >= DriveMax) return FS_FATAL_INVALIDUNIT; if (nUnit >= m_nUnits) - return FS_INVALIDFUNC; // レジューム後に無効なドライブでmint操作時に白帯を出さないよう改良 + return FS_INVALIDFUNC; // Avoid triggering a fatal error in mint when resuming with an invalid drive switch (pCtrlDrive->status) { - case 0: // 状態検査 - case 9: // 状態検査2 + case 0: // Inspect status + case 9: // Inspect status 2 pCtrlDrive->status = (BYTE)m_cEntry.GetStatus(nUnit); return pCtrlDrive->status; - case 1: // イジェクト - case 2: // イジェクト禁止1 (未実装) - case 3: // イジェクト許可1 (未実装) - case 4: // メディア未挿入時にLED点滅 (未実装) - case 5: // メディア未挿入時にLED消灯 (未実装) - case 6: // イジェクト禁止2 (未実装) - case 7: // イジェクト許可2 (未実装) + case 1: // Eject + case 2: // Eject forbidden 1 (not implemented) + case 3: // Eject allowed 1 (not implemented) + case 4: // Flash LED when media is not inserted (not implemented) + case 5: // Turn off LED when media is not inserted (not implemented) + case 6: // Eject forbidden 2 (not implemented) + case 7: // Eject allowed 2 (not implemented) return 0; - case 8: // イジェクト検査 + case 8: // Eject inspection return 1; } @@ -3776,17 +3656,17 @@ int CFileSys::CtrlDrive(DWORD nUnit, Human68k::ctrldrive_t* pCtrlDrive) //--------------------------------------------------------------------------- // -/// $52 - DPB取得 +/// $52 - Get DPB /// -/// レジューム後にDPBが取得できないとHuman68k内部でドライブが消滅するため、 -/// 範囲外のユニットでもとにかく正常系として処理する。 +/// If DPB cannot be acquired after resuming, the drive will be torn down internally in Human68k. +/// Therefore, treat even a unit out of bounds as normal operation. // //--------------------------------------------------------------------------- int CFileSys::GetDPB(DWORD nUnit, Human68k::dpb_t* pDpb) { ASSERT(pDpb); - // ユニットチェック + // Unit check if (nUnit >= DriveMax) return FS_FATAL_INVALIDUNIT; @@ -3795,27 +3675,27 @@ int CFileSys::GetDPB(DWORD nUnit, Human68k::dpb_t* pDpb) if (nUnit < m_nUnits) { media = m_cEntry.GetMediaByte(nUnit); - // セクタ情報獲得 + // Acquire sector data if (m_cEntry.GetCapacityCache(nUnit, &cap) == FALSE) { - // 手動イジェクトだとメディアチェックをすり抜けるためここで捕捉 + // Carry out an extra media check here because it may be skipped when doing a manual eject if (m_cEntry.isEnable(nUnit) == FALSE) goto none; - // メディアチェック + // Media check if (m_cEntry.isMediaOffline(nUnit)) goto none; - // ドライブ状態取得 + // Get drive status m_cEntry.GetCapacity(nUnit, &cap); } } else { none: - cap.clusters = 4; // まっったく問題ないッスよ? + cap.clusters = 4; // This is totally fine, right? cap.sectors = 64; cap.bytes = 512; } - // シフト数計算 + // Calculate number of shifts DWORD nSize = 1; DWORD nShift = 0; for (;;) { @@ -3825,102 +3705,102 @@ int CFileSys::GetDPB(DWORD nUnit, Human68k::dpb_t* pDpb) nShift++; } - // セクタ番号計算 + // Sector number calculation // - // 以下の順に並べる。 - // クラスタ0: 未使用 - // クラスタ1: FAT - // クラスタ2: ルートディレクトリ - // クラスタ3: データ領域(擬似セクタ) + // In the following order: + // Cluster 0: Unused + // Cluster 1: FAT + // Cluster 2: Root directory + // Cluster 3: Data memory (pseudo-sector) DWORD nFat = cap.sectors; DWORD nRoot = cap.sectors * 2; DWORD nData = cap.sectors * 3; - // DPB設定 - pDpb->sector_size = (WORD)cap.bytes; // 1セクタ当りのバイト数 + // Set DPB + pDpb->sector_size = (WORD)cap.bytes; // Bytes per sector pDpb->cluster_size = - (BYTE)(cap.sectors - 1); // 1クラスタ当りのセクタ数 - 1 - pDpb->shift = (BYTE)nShift; // クラスタ→セクタのシフト数 - pDpb->fat_sector = (WORD)nFat; // FAT の先頭セクタ番号 - pDpb->fat_max = 1; // FAT 領域の個数 - pDpb->fat_size = (BYTE)cap.sectors; // FAT の占めるセクタ数(複写分を除く) + (BYTE)(cap.sectors - 1); // Sectors per cluster - 1 + pDpb->shift = (BYTE)nShift; // Number of cluster → sector shifts + pDpb->fat_sector = (WORD)nFat; // First FAT sector number + pDpb->fat_max = 1; // Number of FAT memory spaces + pDpb->fat_size = (BYTE)cap.sectors; // Number of sectors controlled by FAT (excluding copies) pDpb->file_max = - (WORD)(cap.sectors * cap.bytes / 0x20); // ルートディレクトリに入るファイルの個数 - pDpb->data_sector = (WORD)nData; // データ領域の先頭セクタ番号 - pDpb->cluster_max = (WORD)cap.clusters; // 総クラスタ数 + 1 - pDpb->root_sector = (WORD)nRoot; // ルートディレクトリの先頭セクタ番号 - pDpb->media = media; // メディアバイト + (WORD)(cap.sectors * cap.bytes / 0x20); // Number of files in the root directory + pDpb->data_sector = (WORD)nData; // First sector number of data memory + pDpb->cluster_max = (WORD)cap.clusters; // Total number of clusters + 1 + pDpb->root_sector = (WORD)nRoot; // First sector number of the root directory + pDpb->media = media; // Media byte return 0; } //--------------------------------------------------------------------------- // -/// $53 - セクタ読み込み +/// $53 - Read sector /// -/// セクタは疑似的に構築したものを使用する。 -/// バッファサイズは$200バイト固定。 +/// We use pseudo-sectors. +/// Buffer size is hard coded to $200 byte. // //--------------------------------------------------------------------------- int CFileSys::DiskRead(DWORD nUnit, BYTE* pBuffer, DWORD nSector, DWORD nSize) { ASSERT(pBuffer); - // ユニットチェック + // Unit check if (nUnit >= DriveMax) return FS_FATAL_INVALIDUNIT; if (nUnit >= m_nUnits) - return FS_FATAL_MEDIAOFFLINE; // レジューム後に無効なドライブ上で発生 + return FS_FATAL_MEDIAOFFLINE; // This occurs when resuming with an invalid drive - // メディアチェック + // Media check if (m_cEntry.isMediaOffline(nUnit)) return FS_FATAL_MEDIAOFFLINE; - // セクタ数1以外の場合はエラー + // Throw error if number of sectors exceed 1 if (nSize != 1) return FS_INVALIDPRM; - // セクタ情報獲得 + // Acquire sector data Human68k::capacity_t cap; if (m_cEntry.GetCapacityCache(nUnit, &cap) == FALSE) { - // ドライブ状態取得 + // Get drive status m_cEntry.GetCapacity(nUnit, &cap); } - // 擬似ディレクトリエントリへのアクセス + // Access pseudo-directory entry CHostFiles* pHostFiles = m_cFiles.Search(nSector); if (pHostFiles) { - // 擬似ディレクトリエントリを生成 + // Generate pseudo-directory entry Human68k::dirent_t* dir = (Human68k::dirent_t*)pBuffer; memcpy(pBuffer, pHostFiles->GetEntry(), sizeof(*dir)); memset(pBuffer + sizeof(*dir), 0xE5, 0x200 - sizeof(*dir)); - // 擬似ディレクトリエントリ内にファイル実体を指す擬似セクタ番号を記録 - // なお、lzdsysでは以下の式で読み込みセクタ番号を算出している。 + // Register the pseudo-sector number that points to the file entity inside the pseudo-directory. + // Note that in lzdsys the sector number to read is calculated by the following formula: // (dirent.cluster - 2) * (dpb.cluster_size + 1) + dpb.data_sector - /// @warning リトルエンディアン専用 - dir->cluster = (WORD)(m_nHostSectorCount + 2); // 擬似セクタ番号 - m_nHostSectorBuffer[m_nHostSectorCount] = nSector; // 擬似セクタの指す実体 + /// @warning little endian only + dir->cluster = (WORD)(m_nHostSectorCount + 2); // Pseudo-sector number + m_nHostSectorBuffer[m_nHostSectorCount] = nSector; // Entity that points to the pseudo-sector m_nHostSectorCount++; m_nHostSectorCount %= XM6_HOST_PSEUDO_CLUSTER_MAX; return 0; } - // クラスタ番号からセクタ番号を算出 + // Calculate the sector number from the cluster number DWORD n = nSector - (3 * cap.sectors); DWORD nMod = 1; if (cap.sectors) { - // メディアが存在しない場合はcap.sectorsが0になるので注意 + // Beware that cap.sectors becomes 0 when media does not exist nMod = n % cap.sectors; n /= cap.sectors; } - // ファイル実体へのアクセス + // Access the file entity if (nMod == 0 && n < XM6_HOST_PSEUDO_CLUSTER_MAX) { - pHostFiles = m_cFiles.Search(m_nHostSectorBuffer[n]); // 実体を検索 + pHostFiles = m_cFiles.Search(m_nHostSectorBuffer[n]); // Find entity if (pHostFiles) { - // 擬似セクタを生成 + // Generate pseudo-sector CHostFcb f; f.SetFilename(pHostFiles->GetPath()); f.SetMode(Human68k::OP_READ); @@ -3941,28 +3821,28 @@ int CFileSys::DiskRead(DWORD nUnit, BYTE* pBuffer, DWORD nSector, DWORD nSize) //--------------------------------------------------------------------------- // -/// $54 - セクタ書き込み +/// $54 - Write sector // //--------------------------------------------------------------------------- int CFileSys::DiskWrite(DWORD nUnit) { - // ユニットチェック + // Unit check if (nUnit >= DriveMax) return FS_FATAL_INVALIDUNIT; ASSERT(nUnit < m_nUnits); if (nUnit >= m_nUnits) - return FS_FATAL_MEDIAOFFLINE; // 念のため + return FS_FATAL_MEDIAOFFLINE; // Just in case - // メディアチェック + // Media check if (m_cEntry.isMediaOffline(nUnit)) return FS_FATAL_MEDIAOFFLINE; - // 書き込み禁止チェック + // Write-protect check if (m_cEntry.isWriteProtect(nUnit)) return FS_FATAL_WRITEPROTECT; - // 現実を突きつける + // Thrust at reality return FS_INVALIDPRM; } @@ -3975,49 +3855,49 @@ int CFileSys::Ioctrl(DWORD nUnit, DWORD nFunction, Human68k::ioctrl_t* pIoctrl) { ASSERT(pIoctrl); - // ユニットチェック + // Unit check if (nUnit >= DriveMax) return FS_FATAL_INVALIDUNIT; if (nUnit >= m_nUnits) - return FS_INVALIDFUNC; // レジューム後に無効なドライブでmint操作時に白帯を出さないよう改良 + return FS_INVALIDFUNC; // Avoid triggering a fatal error in mint when resuming with an invalid drive switch (nFunction) { case 0: - // メディアバイトの獲得 + // Acquire media byte pIoctrl->media = m_cEntry.GetMediaByte(nUnit); return 0; case 1: - // Human68k互換のためのダミー + // Dummy for Human68k compatibility pIoctrl->param = -1; return 0; case 2: switch (pIoctrl->param) { case (DWORD)-1: - // メディア再認識 + // Re-identify media m_cEntry.isMediaOffline(nUnit); return 0; case 0: case 1: - // Human68k互換のためのダミー + // Dummy for Human68k compatibility return 0; } break; case (DWORD)-1: - // 常駐判定 + // Resident evaluation memcpy(pIoctrl->buffer, "WindrvXM", 8); return 0; case (DWORD)-2: - // オプション設定 + // Set options SetOption(pIoctrl->param); return 0; case (DWORD)-3: - // オプション獲得 + // Get options pIoctrl->param = GetOption(); return 0; } @@ -4027,40 +3907,40 @@ int CFileSys::Ioctrl(DWORD nUnit, DWORD nFunction, Human68k::ioctrl_t* pIoctrl) //--------------------------------------------------------------------------- // -/// $56 - フラッシュ +/// $56 - Flush // //--------------------------------------------------------------------------- int CFileSys::Flush(DWORD nUnit) { - // ユニットチェック + // Unit check if (nUnit >= DriveMax) return FS_FATAL_INVALIDUNIT; if (nUnit >= m_nUnits) - return FS_INVALIDFUNC; // レジューム後に無効なドライブでmintからコマンドを実行し戻る時に白帯を出さないよう改良 + return FS_INVALIDFUNC; // Avoid triggering a fatal error returning from a mint command when resuming with an invalid drive - // 常に成功 + // Always succeed return 0; } //--------------------------------------------------------------------------- // -/// $57 - メディア交換チェック +/// $57 - Media change check // //--------------------------------------------------------------------------- int CFileSys::CheckMedia(DWORD nUnit) { - // ユニットチェック + // Unit check if (nUnit >= DriveMax) return FS_FATAL_INVALIDUNIT; if (nUnit >= m_nUnits) - return FS_INVALIDFUNC; // レジューム後に無効なドライブでmint操作時に白帯を出さないよう改良 + return FS_INVALIDFUNC; // Avoid triggering a fatal error in mint when resuming with an invalid drive - // メディア交換チェック + // Media change check BOOL bResult = m_cEntry.CheckMedia(nUnit); - // メディア未挿入ならエラーとする + // Throw error when media is not inserted if (bResult == FALSE) { return FS_INVALIDFUNC; } @@ -4070,36 +3950,36 @@ int CFileSys::CheckMedia(DWORD nUnit) //--------------------------------------------------------------------------- // -/// $58 - 排他制御 +/// $58 - Lock // //--------------------------------------------------------------------------- int CFileSys::Lock(DWORD nUnit) { - // ユニットチェック + // Unit check if (nUnit >= DriveMax) return FS_FATAL_INVALIDUNIT; ASSERT(nUnit < m_nUnits); if (nUnit >= m_nUnits) - return FS_FATAL_MEDIAOFFLINE; // 念のため + return FS_FATAL_MEDIAOFFLINE; // Just in case - // メディアチェック + // Media check if (m_cEntry.isMediaOffline(nUnit)) return FS_FATAL_MEDIAOFFLINE; - // 常に成功 + // Always succeed return 0; } //--------------------------------------------------------------------------- // -/// オプション設定 +/// Set options // //--------------------------------------------------------------------------- void CFileSys::SetOption(DWORD nOption) { - // オプション設定変更でキャッシュクリア + // Clear cache when option settings change if (m_nOption ^ nOption) m_cEntry.CleanCache(); @@ -4109,14 +3989,14 @@ void CFileSys::SetOption(DWORD nOption) //--------------------------------------------------------------------------- // -/// オプション初期化 +/// Initialize options // //--------------------------------------------------------------------------- void CFileSys::InitOption(const Human68k::argument_t* pArgument) { ASSERT(pArgument); - // ドライブ数を初期化 + // Initialize number of drives m_nDrives = 0; const BYTE* pp = pArgument->buf; @@ -4136,7 +4016,7 @@ void CFileSys::InitOption(const Human68k::argument_t* pArgument) } else if (c == '-') { nMode = 0; } else if (c == '/') { - // デフォルトベースパスの指定 + // Specify default base path if (m_nDrives < DriveMax) { p--; strcpy(m_szBase[m_nDrives], (const char *)p); @@ -4145,7 +4025,7 @@ void CFileSys::InitOption(const Human68k::argument_t* pArgument) pp += strlen((const char*)pp) + 1; continue; } else { - // オプション指定ではないので次へ + // Continue since no option is specified pp += strlen((const char*)pp) + 1; continue; } @@ -4185,7 +4065,7 @@ void CFileSys::InitOption(const Human68k::argument_t* pArgument) pp = p; } - // オプション設定 + // Set options if (nOption != m_nOption) { SetOption(nOption); } @@ -4193,26 +4073,26 @@ void CFileSys::InitOption(const Human68k::argument_t* pArgument) //--------------------------------------------------------------------------- // -/// ボリュームラベル取得 +/// Get volume label // //--------------------------------------------------------------------------- BOOL CFileSys::FilesVolume(DWORD nUnit, Human68k::files_t* pFiles) { ASSERT(pFiles); - // ボリュームラベル取得 + // Get volume label TCHAR szVolume[32]; BOOL bResult = m_cEntry.GetVolumeCache(nUnit, szVolume); if (bResult == FALSE) { - // 手動イジェクトだとメディアチェックをすり抜けるためここで捕捉 + // Carry out an extra media check here because it may be skipped when doing a manual eject if (m_cEntry.isEnable(nUnit) == FALSE) return FALSE; - // メディアチェック + // Media check if (m_cEntry.isMediaOffline(nUnit)) return FALSE; - // ボリュームラベル取得 + // Get volume label m_cEntry.GetVolume(nUnit, szVolume); } if (szVolume[0] == _T('\0')) diff --git a/src/raspberrypi/devices/cfilesystem.h b/src/raspberrypi/devices/cfilesystem.h index ba66c088..21f9593a 100644 --- a/src/raspberrypi/devices/cfilesystem.h +++ b/src/raspberrypi/devices/cfilesystem.h @@ -127,7 +127,7 @@ namespace Human68k { }; struct namests_t { - BYTE wildcard; ///< Wildcard array + BYTE wildcard; ///< Wildcard character length BYTE drive; ///< Drive number BYTE path[65]; ///< Path (subdirectory +/) BYTE name[8]; ///< File name (PADDING 0x20) @@ -332,45 +332,44 @@ enum { // Bit24~30 Duplicate file identification mark 0:Automatic 1~127:Chars }; -/// ファイルシステム動作フラグ +/// File system operational flag /** -通常は0にする。リードオンリーでマウントしたいドライブの場合は1にする。 -それ以外の値は将来のための予約とする。 -判定が困難なデバイス(自作USBストレージとか)のための保険用。 +Normal is 0. Becomes 1 if attempting to mount in read-only mode. +Reserving the other values for future use. +Insurance against hard-to-detect devices such as homemade USB storage. */ enum { - FSFLAG_WRITE_PROTECT = 0x00000001, ///< Bit0: 強制書き込み禁止 - FSFLAG_REMOVABLE = 0x00000002, ///< Bit1: 強制リムーバブルメディア - FSFLAG_MANUAL = 0x00000004, ///< Bit2: 強制手動イジェクト + FSFLAG_WRITE_PROTECT = 0x00000001, ///< Bit0: Force write protect + FSFLAG_REMOVABLE = 0x00000002, ///< Bit1: Force removable media + FSFLAG_MANUAL = 0x00000004, ///< Bit2: Force manual eject }; //=========================================================================== // -/// まるっとリングリスト +/// Full ring list /// -/// 先頭(root.next)が最も新しいオブジェクト。 -/// 末尾(root.prev)が最も古い/未使用オブジェクト。 -/// コード効率追求のため、delete時は必ずポインタをアップキャストすること。 +/// First (root.next) is the most recent object. +/// Last (root.prev) is the oldest / unused object. +/// For code optimization purposes, always upcast the pointer when deleting. // //=========================================================================== class CRing { public: - // 基本ファンクション - CRing() { Init(); } ///< デフォルトコンストラクタ - ~CRing() { Remove(); } ///< デストラクタ final - void Init() { next = prev = this; } ///< 初期化 + CRing() { Init(); } + ~CRing() { Remove(); } + void Init() { next = prev = this; } - CRing* Next() const { return next; } ///< 次の要素を取得 - CRing* Prev() const { return prev; } ///< 前の要素を取得 + CRing* Next() const { return next; } ///< Get the next element + CRing* Prev() const { return prev; } ///< Get the previous element void Insert(CRing* pRoot) { - // 該当オブジェクトを切り離し + // Separate the relevant objects ASSERT(next); ASSERT(prev); next->prev = prev; prev->next = next; - // リング先頭へ挿入 + // Insert into the beginning of the ring ASSERT(pRoot); ASSERT(pRoot->next); next = pRoot->next; @@ -378,16 +377,16 @@ public: pRoot->next->prev = this; pRoot->next = this; } - ///< オブジェクト切り離し & リング先頭へ挿入 + ///< Separate objects & insert into the beginning of the ring void InsertTail(CRing* pRoot) { - // 該当オブジェクトを切り離し + // Separate the relevant objects ASSERT(next); ASSERT(prev); next->prev = prev; prev->next = next; - // リング末尾へ挿入 + // Insert into the end of the ring ASSERT(pRoot); ASSERT(pRoot->prev); next = pRoot; @@ -395,13 +394,13 @@ public: pRoot->prev->next = this; pRoot->prev = this; } - ///< オブジェクト切り離し & リング末尾へ挿入 + ///< Separate objects & insert into the end of the ring void InsertRing(CRing* pRoot) { if (next == prev) return; - // リング先頭へ挿入 + // Insert into the beginning of the ring ASSERT(pRoot); ASSERT(pRoot->next); pRoot->next->prev = prev; @@ -409,554 +408,537 @@ public: pRoot->next = next; next->prev = pRoot; - // 自分自身を空にする + // Empty self next = prev = this; } - ///< 自分以外のオブジェクト切り離し & リング先頭へ挿入 + ///< Separate objects except self & insert into the beginning of the ring void Remove() { - // 該当オブジェクトを切り離し + // Separate the relevant objects ASSERT(next); ASSERT(prev); next->prev = prev; prev->next = next; - // 安全のため自分自身を指しておく (何度切り離しても問題ない) + // To be safe, assign self (nothing stops you from separating any number of times) next = prev = this; } - ///< オブジェクト切り離し + ///< Separate objects private: - CRing* next; ///< 次の要素 - CRing* prev; ///< 前の要素 + CRing* next; ///< Next element + CRing* prev; ///< Previous element }; //=========================================================================== // -/// ディレクトリエントリ ファイル名 +/// Directory Entry: File Name // //=========================================================================== class CHostFilename { public: - // 基本ファンクション - CHostFilename(); ///< デフォルトコンストラクタ - static size_t Offset() { return offsetof(CHostFilename, m_szHost); } ///< オフセット位置取得 + CHostFilename(); + static size_t Offset() { return offsetof(CHostFilename, m_szHost); } ///< Get offset location - void SetHost(const TCHAR* szHost); ///< ホスト側の名称を設定 - const TCHAR* GetHost() const { return m_szHost; } ///< ホスト側の名称を取得 - void ConvertHuman(int nCount = -1); ///< Human68k側の名称を変換 - void CopyHuman(const BYTE* szHuman); ///< Human68k側の名称を複製 - BOOL isReduce() const; ///< Human68k側の名称が加工されたか調査 - BOOL isCorrect() const { return m_bCorrect; } ///< Human68k側のファイル名規則に合致しているか調査 - const BYTE* GetHuman() const { return m_szHuman; } ///< Human68kファイル名を取得 + void SetHost(const TCHAR* szHost); ///< Set the name of the host + const TCHAR* GetHost() const { return m_szHost; } ///< Get the name of the host + void ConvertHuman(int nCount = -1); ///< Convert the Human68k name + void CopyHuman(const BYTE* szHuman); ///< Copy the Human68k name + BOOL isReduce() const; ///< Inspect if the Human68k name is generated + BOOL isCorrect() const { return m_bCorrect; } ///< Inspect if the Human68k file name adhers to naming rules + const BYTE* GetHuman() const { return m_szHuman; } ///< Get Human68k file name const BYTE* GetHumanLast() const - { return m_pszHumanLast; } ///< Human68kファイル名を取得 - const BYTE* GetHumanExt() const { return m_pszHumanExt; }///< Human68kファイル名を取得 - void SetEntryName(); ///< Human68kディレクトリエントリを設定 + { return m_pszHumanLast; } ///< Get Human68k file name + const BYTE* GetHumanExt() const { return m_pszHumanExt; }///< Get Human68k file name + void SetEntryName(); ///< Set Human68k directory entry void SetEntryAttribute(BYTE nHumanAttribute) - { m_dirHuman.attr = nHumanAttribute; } ///< Human68kディレクトリエントリを設定 + { m_dirHuman.attr = nHumanAttribute; } ///< Set Human68k directory entry void SetEntrySize(DWORD nHumanSize) - { m_dirHuman.size = nHumanSize; } ///< Human68kディレクトリエントリを設定 + { m_dirHuman.size = nHumanSize; } ///< Set Human68k directory entry void SetEntryDate(WORD nHumanDate) - { m_dirHuman.date = nHumanDate; } ///< Human68kディレクトリエントリを設定 + { m_dirHuman.date = nHumanDate; } ///< Set Human68k directory entry void SetEntryTime(WORD nHumanTime) - { m_dirHuman.time = nHumanTime; } ///< Human68kディレクトリエントリを設定 + { m_dirHuman.time = nHumanTime; } ///< Set Human68k directory entry void SetEntryCluster(WORD nHumanCluster) - { m_dirHuman.cluster = nHumanCluster; } ///< Human68kディレクトリエントリを設定 + { m_dirHuman.cluster = nHumanCluster; } ///< Set Human68k directory entry const Human68k::dirent_t* GetEntry() const - { return &m_dirHuman; } ///< Human68kディレクトリエントリを取得 - BOOL CheckAttribute(DWORD nHumanAttribute) const; ///< Human68kディレクトリエントリの属性判定 + { return &m_dirHuman; } ///< Get Human68k directory entry + BOOL CheckAttribute(DWORD nHumanAttribute) const; ///< Determine Human68k directory entry attributes BOOL isSameEntry(const Human68k::dirent_t* pdirHuman) const { ASSERT(pdirHuman); return memcmp(&m_dirHuman, pdirHuman, sizeof(m_dirHuman)) == 0; } - ///< Human68kディレクトリエントリの一致判定 + ///< Determine Human68k directory entry match - // パス名操作 - static const BYTE* SeparateExt(const BYTE* szHuman); ///< Human68kファイル名から拡張子を分離 + // Path name operations + static const BYTE* SeparateExt(const BYTE* szHuman); ///< Extract extension from Human68k file name private: static BYTE* CopyName(BYTE* pWrite, const BYTE* pFirst, const BYTE* pLast); - ///< Human68k側のファイル名要素をコピー + ///< Copy Human68k file name elements - const BYTE* m_pszHumanLast; ///< 該当エントリのHuman68k内部名の終端位置 - const BYTE* m_pszHumanExt; ///< 該当エントリのHuman68k内部名の拡張子位置 - BOOL m_bCorrect; ///< 該当エントリのHuman68k内部名が正しければ真 - BYTE m_szHuman[24]; ///< 該当エントリのHuman68k内部名 - Human68k::dirent_t m_dirHuman; ///< 該当エントリのHuman68k全情報 - TCHAR m_szHost[FILEPATH_MAX]; ///< 該当エントリのホスト側の名称 (可変長) + const BYTE* m_pszHumanLast; ///< Last position of the Human68k internal name of the relevant entry + const BYTE* m_pszHumanExt; ///< Position of the extension of the Human68k internal name of the relevant entry + BOOL m_bCorrect; ///< TRUE if the relevant entry of the Human68k internal name is correct + BYTE m_szHuman[24]; ///< Human68k internal name of the relevant entry + Human68k::dirent_t m_dirHuman; ///< All information for the Human68k relevant entry + TCHAR m_szHost[FILEPATH_MAX]; ///< The host name of the relevant entry (variable length) }; //=========================================================================== // -/// ディレクトリエントリ パス名 +/// Directory entry: path name /// -/// Human68k側のパス名は、必ず先頭が/で始まり、末尾が/で終わる。 -/// ユニット番号は持たない。 -/// 高速化のため、ホスト側の名称にはベースパス部分も含む。 +/// A file path in Human68k always begings with / and ends with / +/// They don't hold unit numbers. +/// Include the base path part of the name on the host side for a performance boost. // //=========================================================================== /** @note -ほとんどのHuman68kのアプリは、ファイルの更新等によってディレクトリエ -ントリに変更が生じた際、親ディレクトリのタイムスタンプは変化しないも -のと想定して作られている。 -ところがホスト側のファイルシステムでは親ディレクトリのタイムスタンプ -も変化してしまうものが主流となってしまっている。 +Most Human68k applications are written in a way that expects time stamps not to +get updated for new directories created as a result of file operations, which +triggers updates to directory entires. +However, on the host file system, new directories do typically get an updated time stamp. -このため、ディレクトリのコピー等において、アプリ側は正確にタイムスタ -ンプ情報などを設定しているにもかかわらず、実行結果では時刻情報が上書 -きされてしまうという惨劇が起きてしまう。 +The unfortunate outcome is that when copying a directory for instance, the time stamp +will get overwritten even if the application did not intend for the time stamp to get updated. -そこでディレクトリキャッシュ内にFATタイムスタンプのエミュレーション -機能を実装した。ホスト側のファイルシステムの更新時にタイムスタンプ情 -報を復元することでHuman68k側の期待する結果と一致させる。 +Here follows an implementation of a directory cache FAT time stamp emulation feature. +At the time of a file system update on the host side, time stamp information will be restored +in order to achieve expected behavior on the Human68k side. */ class CHostPath: public CRing { - /// メモリ管理用 + /// For memory management struct ring_t { - CRing r; ///< 円環 - CHostFilename f; ///< 実体 + CRing r; + CHostFilename f; }; public: - /// 検索用バッファ + /// Search buffer struct find_t { - DWORD count; ///< 検索実行回数 + 1 (0のときは以下の値は無効) - DWORD id; ///< 次回検索を続行するパスのエントリ識別ID - const ring_t* pos; ///< 次回検索を続行する位置 (識別ID一致時) - Human68k::dirent_t entry; ///< 次回検索を続行するエントリ内容 + DWORD count; ///< Search execution count + 1 (When 0 the below value is invalid) + DWORD id; ///< Entry unique ID for the path of the next search + const ring_t* pos; ///< Position of the next search (When identical to unique ID) + Human68k::dirent_t entry; ///< Contents of the next seach entry - void Clear() { count = 0; } ///< 初期化 + void Clear() { count = 0; } ///< Initialize }; - // 基本ファンクション - CHostPath(); ///< デフォルトコンストラクタ - ~CHostPath(); ///< デストラクタ final - void Clean(); ///< 再利用のための初期化 + CHostPath(); + ~CHostPath(); + void Clean(); ///< Initialialize for reuse - void SetHuman(const BYTE* szHuman); ///< Human68k側の名称を直接指定する - void SetHost(const TCHAR* szHost); ///< ホスト側の名称を直接指定する - BOOL isSameHuman(const BYTE* szHuman) const; ///< Human68k側の名称を比較する - BOOL isSameChild(const BYTE* szHuman) const; ///< Human68k側の名称を比較する - const TCHAR* GetHost() const { return m_szHost; } ///< ホスト側の名称の獲得 + void SetHuman(const BYTE* szHuman); ///< Directly specify the name on the Human68k side + void SetHost(const TCHAR* szHost); ///< Directly specify the name on the host side + BOOL isSameHuman(const BYTE* szHuman) const; ///< Compare the name on the Human68k side + BOOL isSameChild(const BYTE* szHuman) const; ///< Compare the name on the Human68k side + const TCHAR* GetHost() const { return m_szHost; } ///< Obtain the name on the host side const CHostFilename* FindFilename(const BYTE* szHuman, DWORD nHumanAttribute = Human68k::AT_ALL) const; - ///< ファイル名を検索 + ///< Find file name const CHostFilename* FindFilenameWildcard(const BYTE* szHuman, DWORD nHumanAttribute, find_t* pFind) const; - ///< ファイル名を検索 (ワイルドカード対応) - BOOL isRefresh(); ///< ファイル変更が行なわれたか確認 - void Refresh(); ///< ファイル再構成 - void Backup(); /// ホスト側のタイムスタンプを保存 - void Restore() const; /// ホスト側のタイムスタンプを復元 - void Release(); ///< 更新 + ///< Find file name (with support for wildcards) + BOOL isRefresh(); ///< Check that the file change has been done + void Refresh(); ///< Refresh file + void Backup(); /// Backup the time stamp on the host side + void Restore() const; /// Restore the time stamp on the host side + void Release(); ///< Update - // CHostEntryが利用する外部API - static void InitId() { g_nId = 0; } ///< 識別ID生成用カウンタ初期化 + // CHostEntry is an external API that we use + static void InitId() { g_nId = 0; } ///< Initialize the counter for the unique ID generation private: - static ring_t* Alloc(size_t nLength); ///< ファイル名領域確保 - static void Free(ring_t* pRing); ///< ファイル名領域解放 + static ring_t* Alloc(size_t nLength); ///< Allocate memory for the file name + static void Free(ring_t* pRing); ///< Release memory for the file name static int Compare(const BYTE* pFirst, const BYTE* pLast, const BYTE* pBufFirst, const BYTE* pBufLast); - ///< 文字列比較 (ワイルドカード対応) + ///< Compare string (with support for wildcards) - CRing m_cRing; ///< CHostFilename連結用 - time_t m_tBackup; ///< 時刻復元用 - BOOL m_bRefresh; ///< 更新フラグ - DWORD m_nId; ///< 識別ID (値が変化した場合は更新を意味する) - BYTE m_szHuman[HUMAN68K_PATH_MAX]; ///< 該当エントリのHuman68k内部名 - TCHAR m_szHost[FILEPATH_MAX]; ///< 該当エントリのホスト側の名称 + CRing m_cRing; ///< For CHostFilename linking + time_t m_tBackup; ///< For time stamp restoration + BOOL m_bRefresh; ///< Refresh flag + DWORD m_nId; ///< Unique ID (When the value has changed, it means an update has been made) + BYTE m_szHuman[HUMAN68K_PATH_MAX]; ///< The internal Human68k name for the relevant entry + TCHAR m_szHost[FILEPATH_MAX]; ///< The host side name for the relevant entry - static DWORD g_nId; ///< 識別ID生成用カウンタ + static DWORD g_nId; ///< Counter for the unique ID generation }; //=========================================================================== // -/// ファイル検索処理 +/// File search processing /// -/// Human68k側のファイル名を内部Unicodeで処理するのは正直キツい。と -/// いうわけで、全てBYTEに変換して処理する。変換処理はディレクトリエ -/// ントリキャッシュが一手に担い、WINDRV側はすべてシフトJISのみで扱 -/// えるようにする。 -/// また、Human68k側名称は、完全にベースパス指定から独立させる。 +/// It's pretty much impossible to process Human68k file names as Unicode internally. +/// So, we carry out binary conversion for processing. We leave it up to the +/// directory entry cache to handle the conversion, which allows WINDRV to read +/// everything as Shift-JIS. Additionally, it allows Human68k names to be +/// fully independent of base path assignments. /// -/// ファイルを扱う直前に、ディレクトリエントリのキャッシュを生成する。 -/// ディレクトリエントリの生成処理は高コストのため、一度生成したエントリは -/// 可能な限り維持して使い回す。 +/// We create directory entry cache just before file handling. +/// Since creating directory entires is very costly, we try to reuse created entries +/// as much as humanly possible. /// -/// ファイル検索は3方式。すべてCHostFiles::Find()で処理する。 -/// 1. パス名のみ検索 属性はディレクトリのみ _CHKDIR _CREATE -/// 2. パス名+ファイル名+属性の検索 _OPEN -/// 3. パス名+ワイルドカード+属性の検索 _FILES _NFILES -/// 検索結果は、ディレクトリエントリ情報として保持しておく。 +/// There are three kinds of file search. They are all processed in CHostFiles::Find() +/// 1. Search by path name only; the only attribute is 'directory'; _CHKDIR _CREATE +/// 2. Path + file name + attribute search; _OPEN +/// 3. Path + wildcard + attribute search; _FILES _NFILES +/// The search results are kept as directory entry data. // //=========================================================================== class CHostFiles { public: - // 基本ファンクション - CHostFiles() { SetKey(0); Init(); } ///< デフォルトコンストラクタ - void Init(); ///< 初期化 + CHostFiles() { SetKey(0); Init(); } + void Init(); - void SetKey(DWORD nKey) { m_nKey = nKey; } ///< 検索キー設定 - BOOL isSameKey(DWORD nKey) const { return m_nKey == nKey; } ///< 検索キー比較 - void SetPath(const Human68k::namests_t* pNamests); ///< パス名・ファイル名を内部で生成 - BOOL isRootPath() const { return m_szHumanPath[1] == '\0'; } ///< ルートディレクトリ判定 - void SetPathWildcard() { m_nHumanWildcard = 1; } ///< ワイルドカードによるファイル検索を有効化 - void SetPathOnly() { m_nHumanWildcard = 0xFF; } ///< パス名のみを有効化 - BOOL isPathOnly() const { return m_nHumanWildcard == 0xFF; } ///< パス名のみ設定か判定 + void SetKey(DWORD nKey) { m_nKey = nKey; } ///< Set search key + BOOL isSameKey(DWORD nKey) const { return m_nKey == nKey; } ///< Compare search key + void SetPath(const Human68k::namests_t* pNamests); ///< Create path and file name internally + BOOL isRootPath() const { return m_szHumanPath[1] == '\0'; } ///< Check if root directory + void SetPathWildcard() { m_nHumanWildcard = 1; } ///< Enable file search using wildcards + void SetPathOnly() { m_nHumanWildcard = 0xFF; } ///< Enable only path names + BOOL isPathOnly() const { return m_nHumanWildcard == 0xFF; } ///< Check if set to only path names void SetAttribute(DWORD nHumanAttribute) { m_nHumanAttribute = nHumanAttribute; } - ///< 検索属性を設定 - BOOL Find(DWORD nUnit, class CHostEntry* pEntry); ///< Human68k側でファイルを検索しホスト側の情報を生成 - const CHostFilename* Find(CHostPath* pPath); ///< ファイル名検索 - void SetEntry(const CHostFilename* pFilename); ///< Human68k側の検索結果保存 - void SetResult(const TCHAR* szPath); ///< ホスト側の名称を設定 - void AddResult(const TCHAR* szPath); ///< ホスト側の名称にファイル名を追加 - void AddFilename(); ///< ホスト側の名称にHuman68kの新規ファイル名を追加 + ///< Set search attribute + BOOL Find(DWORD nUnit, class CHostEntry* pEntry); ///< Find files on the Human68k side, generating data on the host side + const CHostFilename* Find(CHostPath* pPath); ///< Find file name + void SetEntry(const CHostFilename* pFilename); ///< Store search results on the Human68k side + void SetResult(const TCHAR* szPath); ///< Set names on the host side + void AddResult(const TCHAR* szPath); ///< Add file name to the name on the host side + void AddFilename(); ///< Add the new Human68k file name to the name on the host side - const TCHAR* GetPath() const { return m_szHostResult; } ///< ホスト側の名称を取得 + const TCHAR* GetPath() const { return m_szHostResult; } ///< Get the name on the host side - const Human68k::dirent_t* GetEntry() const { return &m_dirHuman; }///< Human68kディレクトリエントリを取得 + const Human68k::dirent_t* GetEntry() const { return &m_dirHuman; }///< Get Human68k directory entry - DWORD GetAttribute() const { return m_dirHuman.attr; } ///< Human68k属性を取得 - WORD GetDate() const { return m_dirHuman.date; } ///< Human68k日付を取得 - WORD GetTime() const { return m_dirHuman.time; } ///< Human68k時刻を取得 - DWORD GetSize() const { return m_dirHuman.size; } ///< Human68kファイルサイズを取得 - const BYTE* GetHumanFilename() const { return m_szHumanFilename; }///< Human68kファイル名を取得 - const BYTE* GetHumanResult() const { return m_szHumanResult; } ///< Human68kファイル名検索結果を取得 - const BYTE* GetHumanPath() const { return m_szHumanPath; } ///< Human68kパス名を取得 + DWORD GetAttribute() const { return m_dirHuman.attr; } ///< Get Human68k attribute + WORD GetDate() const { return m_dirHuman.date; } ///< Get Human68k date + WORD GetTime() const { return m_dirHuman.time; } ///< Get Human68k time + DWORD GetSize() const { return m_dirHuman.size; } ///< Get Human68k file size + const BYTE* GetHumanFilename() const { return m_szHumanFilename; }///< Get Human68k file name + const BYTE* GetHumanResult() const { return m_szHumanResult; } ///< Get Human68k file name search results + const BYTE* GetHumanPath() const { return m_szHumanPath; } ///< Get Human68k path name private: - DWORD m_nKey; ///< Human68kのFILESバッファアドレス 0なら未使用 - DWORD m_nHumanWildcard; ///< Human68kのワイルドカード情報 - DWORD m_nHumanAttribute; ///< Human68kの検索属性 - CHostPath::find_t m_findNext; ///< 次回検索位置情報 - Human68k::dirent_t m_dirHuman; ///< 検索結果 Human68kファイル情報 - BYTE m_szHumanFilename[24]; ///< Human68kのファイル名 - BYTE m_szHumanResult[24]; ///< 検索結果 Human68kファイル名 + DWORD m_nKey; ///< FILES buffer address for Human68k; 0 is unused + DWORD m_nHumanWildcard; ///< Human68k wildcard data + DWORD m_nHumanAttribute; ///< Human68k search attribute + CHostPath::find_t m_findNext; ///< Next search location data + Human68k::dirent_t m_dirHuman; ///< Search results: Human68k file data + BYTE m_szHumanFilename[24]; ///< Human68k file name + BYTE m_szHumanResult[24]; ///< Search results: Human68k file name BYTE m_szHumanPath[HUMAN68K_PATH_MAX]; - ///< Human68kのパス名 - TCHAR m_szHostResult[FILEPATH_MAX]; ///< 検索結果 ホスト側のフルパス名 + ///< Human68k path name + TCHAR m_szHostResult[FILEPATH_MAX]; ///< Search results: host's full path name }; //=========================================================================== // -/// ファイル検索領域 マネージャ +/// File search memory manager // //=========================================================================== class CHostFilesManager { public: #ifdef _DEBUG - // 基本ファンクション - ~CHostFilesManager(); ///< デストラクタ final + ~CHostFilesManager(); #endif // _DEBUG - void Init(); ///< 初期化 (ドライバ組込み時) - void Clean(); ///< 解放 (起動・リセット時) + void Init(); ///< Initialization (when the driver is installed) + void Clean(); ///< Release (when starting up or resetting) - CHostFiles* Alloc(DWORD nKey); ///< 確保 - CHostFiles* Search(DWORD nKey); ///< 検索 - void Free(CHostFiles* pFiles); ///< 解放 + CHostFiles* Alloc(DWORD nKey); + CHostFiles* Search(DWORD nKey); + void Free(CHostFiles* pFiles); private: - /// メモリ管理用 + /// For memory management struct ring_t { - CRing r; ///< 円環 - CHostFiles f; ///< 実体 + CRing r; + CHostFiles f; }; - CRing m_cRing; ///< CHostFiles連結用 + CRing m_cRing; ///< For attaching to CHostFiles }; //=========================================================================== // -/// FCB処理 +/// FCB processing // //=========================================================================== class CHostFcb { public: - // 基本ファンクション - CHostFcb() { SetKey(0); Init(); } ///< デフォルトコンストラクタ - ~CHostFcb() { Close(); } ///< デストラクタ final - void Init(); ///< 初期化 + CHostFcb() { SetKey(0); Init(); } + ~CHostFcb() { Close(); } + void Init(); - void SetKey(DWORD nKey) { m_nKey = nKey; } ///< 検索キー設定 - BOOL isSameKey(DWORD nKey) const { return m_nKey == nKey; } ///< 検索キー比較 - void SetUpdate() { m_bUpdate = TRUE; } ///< 更新 - BOOL isUpdate() const { return m_bUpdate; } ///< 更新状態取得 - BOOL SetMode(DWORD nHumanMode); ///< ファイルオープンモードを設定 - void SetFilename(const TCHAR* szFilename); ///< ファイル名を設定 - void SetHumanPath(const BYTE* szHumanPath); ///< Human68kパス名を設定 - const BYTE* GetHumanPath() const { return m_szHumanPath; } ///< Human68kパス名を取得 + void SetKey(DWORD nKey) { m_nKey = nKey; } ///< Set search key + BOOL isSameKey(DWORD nKey) const { return m_nKey == nKey; } ///< Compare search key + void SetUpdate() { m_bUpdate = TRUE; } ///< Update + BOOL isUpdate() const { return m_bUpdate; } ///< Get update state + BOOL SetMode(DWORD nHumanMode); ///< Set file open mode + void SetFilename(const TCHAR* szFilename); ///< Set file name + void SetHumanPath(const BYTE* szHumanPath); ///< Set Human68k path name + const BYTE* GetHumanPath() const { return m_szHumanPath; } ///< Get Human68k path name - BOOL Create(Human68k::fcb_t* pFcb, DWORD nHumanAttribute, BOOL bForce); ///< ファイル作成 - BOOL Open(); ///< ファイルオープン - BOOL Rewind(DWORD nOffset); ///< ファイルシーク - DWORD Read(BYTE* pBuffer, DWORD nSize); ///< ファイル読み込み - DWORD Write(const BYTE* pBuffer, DWORD nSize); ///< ファイル書き込み - BOOL Truncate(); ///< ファイル切り詰め - DWORD Seek(DWORD nOffset, DWORD nHumanSeek); ///< ファイルシーク - BOOL TimeStamp(DWORD nHumanTime); ///< ファイル時刻設定 - BOOL Close(); ///< ファイルクローズ + BOOL Create(Human68k::fcb_t* pFcb, DWORD nHumanAttribute, BOOL bForce); ///< Create file + BOOL Open(); ///< Open file + BOOL Rewind(DWORD nOffset); ///< Seek file + DWORD Read(BYTE* pBuffer, DWORD nSize); ///< Read file + DWORD Write(const BYTE* pBuffer, DWORD nSize); ///< Write file + BOOL Truncate(); ///< Truncate file + DWORD Seek(DWORD nOffset, DWORD nHumanSeek); ///< Seek file + BOOL TimeStamp(DWORD nHumanTime); ///< Set file time stamp + BOOL Close(); ///< Close file private: - DWORD m_nKey; ///< Human68kのFCBバッファアドレス (0なら未使用) - BOOL m_bUpdate; ///< 更新フラグ - FILE* m_pFile; ///< ホスト側のファイルオブジェクト - const char* m_pszMode; ///< ホスト側のファイルオープンモード - bool m_bFlag; ///< ホスト側のファイルオープンフラグ + DWORD m_nKey; ///< Human68k FCB buffer address (0 if unused) + BOOL m_bUpdate; ///< Update flag + FILE* m_pFile; ///< Host side file object + const char* m_pszMode; ///< Host side file open mode + bool m_bFlag; ///< Host side file open flag BYTE m_szHumanPath[HUMAN68K_PATH_MAX]; - ///< Human68kのパス名 - TCHAR m_szFilename[FILEPATH_MAX]; ///< ホスト側のファイル名 + ///< Human68k path name + TCHAR m_szFilename[FILEPATH_MAX]; ///< Host side file name }; //=========================================================================== // -/// FCB処理 マネージャ +/// FCB processing manager // //=========================================================================== class CHostFcbManager { public: #ifdef _DEBUG - // 基本ファンクション - ~CHostFcbManager(); ///< デストラクタ final + ~CHostFcbManager(); #endif // _DEBUG - void Init(); ///< 初期化 (ドライバ組込み時) - void Clean(); ///< 解放 (起動・リセット時) + void Init(); ///< Initialization (when the driver is installed) + void Clean(); ///< Release (when starting up or resetting) - CHostFcb* Alloc(DWORD nKey); ///< 確保 - CHostFcb* Search(DWORD nKey); ///< 検索 - void Free(CHostFcb* p); ///< 解放 + CHostFcb* Alloc(DWORD nKey); + CHostFcb* Search(DWORD nKey); + void Free(CHostFcb* p); private: - /// メモリ管理用 + /// For memory management struct ring_t { - CRing r; ///< 円環 - CHostFcb f; ///< 実体 + CRing r; + CHostFcb f; }; - CRing m_cRing; ///< CHostFcb連結用 + CRing m_cRing; ///< For attaching to CHostFcb }; //=========================================================================== // -/// ホスト側ドライブ +/// Host side drive /// -/// ドライブ毎に必要な情報の保持に専念し、管理はCHostEntryで行なう。 +/// Keeps the required data for each drive, managed in CHostEntry. // //=========================================================================== class CHostDrv { public: - // 基本ファンクション - CHostDrv(); ///< デフォルトコンストラクタ - ~CHostDrv(); ///< デストラクタ final - void Init(const TCHAR* szBase, DWORD nFlag); ///< 初期化 (デバイス起動とロード) + CHostDrv(); + ~CHostDrv(); + void Init(const TCHAR* szBase, DWORD nFlag); ///< Initialization (device startup and load) - BOOL isWriteProtect() const { return m_bWriteProtect; } ///< 書き込み禁止か? - BOOL isEnable() const { return m_bEnable; } ///< アクセス可能か? - BOOL isMediaOffline(); ///< メディアチェック - BYTE GetMediaByte() const; ///< メディアバイトの取得 - DWORD GetStatus() const; ///< ドライブ状態の取得 - void SetEnable(BOOL bEnable); ///< メディア状態設定 - BOOL CheckMedia(); ///< メディア交換チェック - void Update(); ///< メディア状態更新 - void Eject(); ///< イジェクト - void GetVolume(TCHAR* szLabel); ///< ボリュームラベルの取得 - BOOL GetVolumeCache(TCHAR* szLabel) const; ///< キャッシュからボリュームラベルを取得 - DWORD GetCapacity(Human68k::capacity_t* pCapacity); ///< 容量の取得 - BOOL GetCapacityCache(Human68k::capacity_t* pCapacity) const; ///< キャッシュから容量を取得 + BOOL isWriteProtect() const { return m_bWriteProtect; } + BOOL isEnable() const { return m_bEnable; } ///< Is it accessible? + BOOL isMediaOffline(); + BYTE GetMediaByte() const; + DWORD GetStatus() const; + void SetEnable(BOOL bEnable); ///< Set media status + BOOL CheckMedia(); ///< Check if media was changed + void Update(); ///< Update media status + void Eject(); + void GetVolume(TCHAR* szLabel); ///< Get volume label + BOOL GetVolumeCache(TCHAR* szLabel) const; ///< Get volume label from cache + DWORD GetCapacity(Human68k::capacity_t* pCapacity); + BOOL GetCapacityCache(Human68k::capacity_t* pCapacity) const; ///< Get capacity from cache - // キャッシュ操作 - void CleanCache(); ///< 全てのキャッシュを更新する - void CleanCache(const BYTE* szHumanPath); ///< 指定されたパスのキャッシュを更新する - void CleanCacheChild(const BYTE* szHumanPath); ///< 指定されたパス以下のキャッシュを全て更新する - void DeleteCache(const BYTE* szHumanPath); ///< 指定されたパスのキャッシュを削除する - CHostPath* FindCache(const BYTE* szHuman); ///< 指定されたパスがキャッシュされているか検索する - CHostPath* CopyCache(CHostFiles* pFiles); ///< キャッシュ情報を元に、ホスト側の名称を獲得する - CHostPath* MakeCache(CHostFiles* pFiles); ///< ホスト側の名称の構築に必要な情報をすべて取得する - BOOL Find(CHostFiles* pFiles); ///< ホスト側の名称を検索 (パス名+ファイル名(省略可)+属性) + // Cache operations + void CleanCache(); ///< Update all cache + void CleanCache(const BYTE* szHumanPath); ///< Update cache for the specified path + void CleanCacheChild(const BYTE* szHumanPath); ///< Update all cache below the specified path + void DeleteCache(const BYTE* szHumanPath); ///< Delete the cache for the specified path + CHostPath* FindCache(const BYTE* szHuman); ///< Inspect if the specified path is cached + CHostPath* CopyCache(CHostFiles* pFiles); ///< Acquire the host side name on the basis of cache information + CHostPath* MakeCache(CHostFiles* pFiles); ///< Get all required data to construct a host side name + BOOL Find(CHostFiles* pFiles); ///< Find host side name (path + file name (can be abbreviated) + attribute) private: - // パス名操作 + // Path name operations static const BYTE* SeparateCopyFilename(const BYTE* szHuman, BYTE* szBuffer); - ///< Human68kフルパス名から先頭の要素を分離・コピー + ///< Split and copy the first element of the Human68k full path name - // 排他制御 void Lock() {} void Unlock() {} - /// メモリ管理用 + /// For memory management struct ring_t { - CRing r; ///< 円環 - CHostPath f; ///< 実体 + CRing r; + CHostPath f; }; - BOOL m_bWriteProtect; ///< 書き込み禁止ならTRUE - BOOL m_bEnable; ///< メディアが利用可能ならTRUE - DWORD m_nRing; ///< パス名保持数 - CRing m_cRing; ///< CHostPath連結用 - Human68k::capacity_t m_capCache; ///< セクタ情報キャッシュ sectors == 0 なら未キャッシュ - BOOL m_bVolumeCache; ///< ボリュームラベル読み込み済みならTRUE - TCHAR m_szVolumeCache[24]; ///< ボリュームラベルキャッシュ - TCHAR m_szBase[FILEPATH_MAX]; ///< ベースパス + BOOL m_bWriteProtect; ///< TRUE if write-protected + BOOL m_bEnable; ///< TRUE if media is usable + DWORD m_nRing; ///< Number of stored path names + CRing m_cRing; ///< For attaching to CHostPath + Human68k::capacity_t m_capCache; ///< Sector data cache: if "sectors == 0" then not cached + BOOL m_bVolumeCache; ///< TRUE if the volume label has been read + TCHAR m_szVolumeCache[24]; ///< Volume label cache + TCHAR m_szBase[FILEPATH_MAX]; ///< Base path }; //=========================================================================== // -/// ディレクトリエントリ管理 +/// Directory entry management // //=========================================================================== class CHostEntry { public: - // 基本ファンクション - CHostEntry(); ///< デフォルトコンストラクタ - ~CHostEntry(); ///< デストラクタ final - void Init(); ///< 初期化 (ドライバ組込み時) - void Clean(); ///< 解放 (起動・リセット時) + CHostEntry(); + ~CHostEntry(); + void Init(); ///< Initialization (when the driver is installed) + void Clean(); ///< Release (when starting up or resetting) - // キャッシュ操作 - void CleanCache(); ///< 全てのキャッシュを更新する - void CleanCache(DWORD nUnit); ///< 指定されたユニットのキャッシュを更新する - void CleanCache(DWORD nUnit, const BYTE* szHumanPath); ///< 指定されたパスのキャッシュを更新する - void CleanCacheChild(DWORD nUnit, const BYTE* szHumanPath); ///< 指定されたパス以下のキャッシュを全て更新する - void DeleteCache(DWORD nUnit, const BYTE* szHumanPath); ///< 指定されたパスのキャッシュを削除する - BOOL Find(DWORD nUnit, CHostFiles* pFiles); ///< ホスト側の名称を検索 (パス名+ファイル名(省略可)+属性) - void ShellNotify(DWORD nEvent, const TCHAR* szPath); ///< ホスト側ファイルシステム状態変化通知 + // Cache operations + void CleanCache(); ///< Update all cache + void CleanCache(DWORD nUnit); ///< Update cache for the specified unit + void CleanCache(DWORD nUnit, const BYTE* szHumanPath); ///< Update cache for the specified path + void CleanCacheChild(DWORD nUnit, const BYTE* szHumanPath); ///< Update cache below the specified path + void DeleteCache(DWORD nUnit, const BYTE* szHumanPath); ///< Delete cache for the specified path + BOOL Find(DWORD nUnit, CHostFiles* pFiles); ///< Find host side name (path + file name (can be abbreviated) + attribute) + void ShellNotify(DWORD nEvent, const TCHAR* szPath); ///< Notify status change in the host side file system - // ドライブオブジェクト操作 - void SetDrv(DWORD nUnit, CHostDrv* pDrv); ///< ドライブ設定 - BOOL isWriteProtect(DWORD nUnit) const; ///< 書き込み禁止か? - BOOL isEnable(DWORD nUnit) const; ///< アクセス可能か? - BOOL isMediaOffline(DWORD nUnit); ///< メディアチェック - BYTE GetMediaByte(DWORD nUnit) const; ///< メディアバイトの取得 - DWORD GetStatus(DWORD nUnit) const; ///< ドライブ状態の取得 - BOOL CheckMedia(DWORD nUnit); ///< メディア交換チェック - void Eject(DWORD nUnit); ///< イジェクト - void GetVolume(DWORD nUnit, TCHAR* szLabel); ///< ボリュームラベルの取得 - BOOL GetVolumeCache(DWORD nUnit, TCHAR* szLabel) const; ///< キャッシュからボリュームラベルを取得 - DWORD GetCapacity(DWORD nUnit, Human68k::capacity_t* pCapacity); ///< 容量の取得 + // Drive object operations + void SetDrv(DWORD nUnit, CHostDrv* pDrv); + BOOL isWriteProtect(DWORD nUnit) const; + BOOL isEnable(DWORD nUnit) const; ///< Is it accessible? + BOOL isMediaOffline(DWORD nUnit); + BYTE GetMediaByte(DWORD nUnit) const; + DWORD GetStatus(DWORD nUnit) const; ///< Get drive status + BOOL CheckMedia(DWORD nUnit); ///< Media change check + void Eject(DWORD nUnit); + void GetVolume(DWORD nUnit, TCHAR* szLabel); ///< Get volume label + BOOL GetVolumeCache(DWORD nUnit, TCHAR* szLabel) const; ///< Get volume label from cache + DWORD GetCapacity(DWORD nUnit, Human68k::capacity_t* pCapacity); BOOL GetCapacityCache(DWORD nUnit, Human68k::capacity_t* pCapacity) const; - ///< キャッシュからクラスタサイズを取得 + ///< Get cluster size from cache - /// 定数 enum { - DriveMax = 10 ///< ドライブ最大候補数 + DriveMax = 10 ///< Max number of drive candidates }; private: - CHostDrv* m_pDrv[DriveMax]; ///< ホスト側ドライブオブジェクト - DWORD m_nTimeout; ///< 最後にタイムアウトチェックを行なった時刻 + CHostDrv* m_pDrv[DriveMax]; ///< Host side drive object + DWORD m_nTimeout; ///< Last time a timeout check was carried out }; //=========================================================================== // -/// ホスト側ファイルシステム +/// Host side file system // //=========================================================================== /** @note -現在の見解。 +Current state of affairs: -XM6の設計思想とは反するが、class Windrvまたはclass CWindrvに直接 -class CFileSysへのポインタを持たせる方法を模索するべきである。 -これにより、以下のメリットが得られる。 +While it violates the design philosophy of XM6, we should find a way for +'class Windrv' and 'class CWindrv' to have a direct pointer to 'class CFileSys'. +This way, we get the following benefits. -メリットその1。 -コマンドハンドラの大量のメソッド群を一ヶ所で集中管理できる。 -コマンドハンドラはホスト側の仕様変更などの要因によって激しく変化する -可能性が高いため、メソッドの追加削除や引数の変更などのメンテナンスが -大幅に楽になる。 +Benefit no. 1 +Makes it possible to manage a large number of command handler methods in one place. +There is a high chance the command handlers will change drastically because of +host system architectural changes, so we will save a huge amount of maintenance work +in the long run. -メリットその2。 -仮想関数のテーブル生成・参照処理に関する処理コードを駆逐できる。 -XM6では複数のファイルシステムオブジェクトを同時に使うような実装は -ありえない。つまりファイルシステムオブジェクトにポリモーフィズムは -まったく必要ないどころか、ただクロックの無駄となっているだけである。 +Benefit no. 2 +We would get rid of virtual funcion code for processing table creation and lookup. +It is not feasible to implement code in XM6 for simultaneous use of file system objects. +Therefore file system object polymorphism is a waste of CPU cycles. -試しに変えてみた。実際効率上がった。 -windrv.h内のFILESYS_FAST_STRUCTUREの値を変えてコンパイラの吐くソース -を比較すれば一目瞭然。何故私がこんな長文を書こうと思ったのかを理解で -きるはず。 +I made the change as an experiment. Performance did improve. +The improvement was obvious from looking at the source the compiler spit out +after changing the FILESYS_FAST_STRUCTURE value in windrv.h. +You may understand now why I decided to rant here. -一方ロシアはclass CWindrv内にclass CFileSysを直接設置した。 -(本当はclass CHostを廃止して直接置きたい……良い方法はないものか……) +The easy solution is to put the content of 'class CFileSys' into 'class CWindrv'. +(To be honest, I really want to deprecate 'class CHost'... I wonder if there's a good way...) */ class CFileSys { public: - // 基本ファンクション - CFileSys(); ///< デフォルトコンストラクタ - virtual ~CFileSys() {}; ///< デストラクタ + CFileSys(); + virtual ~CFileSys() {}; - // 初期化・終了 - void Reset(); ///< リセット (全クローズ) - void Init(); ///< 初期化 (デバイス起動とロード) + void Reset(); ///< Reset (close all) + void Init(); ///< Initialization (device startup and load) - // コマンドハンドラ - DWORD InitDevice(const Human68k::argument_t* pArgument); ///< $40 - デバイス起動 - int CheckDir(DWORD nUnit, const Human68k::namests_t* pNamests); ///< $41 - ディレクトリチェック - int MakeDir(DWORD nUnit, const Human68k::namests_t* pNamests); ///< $42 - ディレクトリ作成 - int RemoveDir(DWORD nUnit, const Human68k::namests_t* pNamests); ///< $43 - ディレクトリ削除 + // Command handlers + DWORD InitDevice(const Human68k::argument_t* pArgument); ///< $40 - Device startup + int CheckDir(DWORD nUnit, const Human68k::namests_t* pNamests); ///< $41 - Directory check + int MakeDir(DWORD nUnit, const Human68k::namests_t* pNamests); ///< $42 - Create directory + int RemoveDir(DWORD nUnit, const Human68k::namests_t* pNamests); ///< $43 - Delete directory int Rename(DWORD nUnit, const Human68k::namests_t* pNamests, const Human68k::namests_t* pNamestsNew); - ///< $44 - ファイル名変更 - int Delete(DWORD nUnit, const Human68k::namests_t* pNamests); ///< $45 - ファイル削除 + ///< $44 - Change file name + int Delete(DWORD nUnit, const Human68k::namests_t* pNamests); ///< $45 - Delete file int Attribute(DWORD nUnit, const Human68k::namests_t* pNamests, DWORD nHumanAttribute); - ///< $46 - ファイル属性取得/設定 + ///< $46 - Get / set file attribute int Files(DWORD nUnit, DWORD nKey, const Human68k::namests_t* pNamests, Human68k::files_t* pFiles); - ///< $47 - ファイル検索 - int NFiles(DWORD nUnit, DWORD nKey, Human68k::files_t* pFiles); ///< $48 - ファイル次検索 + ///< $47 - Find file + int NFiles(DWORD nUnit, DWORD nKey, Human68k::files_t* pFiles); ///< $48 - Find next file int Create(DWORD nUnit, DWORD nKey, const Human68k::namests_t* pNamests, Human68k::fcb_t* pFcb, DWORD nHumanAttribute, BOOL bForce); - ///< $49 - ファイル作成 + ///< $49 - Create file int Open(DWORD nUnit, DWORD nKey, const Human68k::namests_t* pNamests, Human68k::fcb_t* pFcb); - ///< $4A - ファイルオープン - int Close(DWORD nUnit, DWORD nKey, Human68k::fcb_t* pFcb); ///< $4B - ファイルクローズ + ///< $4A - Open file + int Close(DWORD nUnit, DWORD nKey, Human68k::fcb_t* pFcb); ///< $4B - Close file int Read(DWORD nKey, Human68k::fcb_t* pFcb, BYTE* pAddress, DWORD nSize); - ///< $4C - ファイル読み込み + ///< $4C - Read file int Write(DWORD nKey, Human68k::fcb_t* pFcb, const BYTE* pAddress, DWORD nSize); - ///< $4D - ファイル書き込み - int Seek(DWORD nKey, Human68k::fcb_t* pFcb, DWORD nSeek, int nOffset); ///< $4E - ファイルシーク + ///< $4D - Write file + int Seek(DWORD nKey, Human68k::fcb_t* pFcb, DWORD nSeek, int nOffset); ///< $4E - Seek file DWORD TimeStamp(DWORD nUnit, DWORD nKey, Human68k::fcb_t* pFcb, DWORD nHumanTime); - ///< $4F - ファイル時刻取得/設定 - int GetCapacity(DWORD nUnit, Human68k::capacity_t* pCapacity); ///< $50 - 容量取得 - int CtrlDrive(DWORD nUnit, Human68k::ctrldrive_t* pCtrlDrive); ///< $51 - ドライブ状態検査/制御 - int GetDPB(DWORD nUnit, Human68k::dpb_t* pDpb); ///< $52 - DPB取得 - int DiskRead(DWORD nUnit, BYTE* pBuffer, DWORD nSector, DWORD nSize); ///< $53 - セクタ読み込み - int DiskWrite(DWORD nUnit); ///< $54 - セクタ書き込み + ///< $4F - Get / set file timestamp + int GetCapacity(DWORD nUnit, Human68k::capacity_t* pCapacity); ///< $50 - Get capacity + int CtrlDrive(DWORD nUnit, Human68k::ctrldrive_t* pCtrlDrive); ///< $51 - Inspect / control drive status + int GetDPB(DWORD nUnit, Human68k::dpb_t* pDpb); ///< $52 - Get DPB + int DiskRead(DWORD nUnit, BYTE* pBuffer, DWORD nSector, DWORD nSize); ///< $53 - Read sectors + int DiskWrite(DWORD nUnit); ///< $54 - Write sectors int Ioctrl(DWORD nUnit, DWORD nFunction, Human68k::ioctrl_t* pIoctrl); ///< $55 - IOCTRL - int Flush(DWORD nUnit); ///< $56 - フラッシュ - int CheckMedia(DWORD nUnit); ///< $57 - メディア交換チェック - int Lock(DWORD nUnit); ///< $58 - 排他制御 + int Flush(DWORD nUnit); ///< $56 - Flush + int CheckMedia(DWORD nUnit); ///< $57 - Media change check + int Lock(DWORD nUnit); ///< $58 - Lock - void SetOption(DWORD nOption); ///< オプション設定 - DWORD GetOption() const { return m_nOption; } ///< オプション取得 - DWORD GetDefault() const { return m_nOptionDefault; } ///< デフォルトオプション取得 - static DWORD GetFileOption() { return g_nOption; } ///< ファイル名変換オプション取得 + void SetOption(DWORD nOption); ///< Set option + DWORD GetOption() const { return m_nOption; } ///< Get option + DWORD GetDefault() const { return m_nOptionDefault; } ///< Get default options + static DWORD GetFileOption() { return g_nOption; } ///< Get file name change option void ShellNotify(DWORD nEvent, const TCHAR* szPath) - { m_cEntry.ShellNotify(nEvent, szPath); } ///< ホスト側ファイルシステム状態変化通知 + { m_cEntry.ShellNotify(nEvent, szPath); } ///< Notify host side file system status change - /// 定数 enum { - DriveMax = CHostEntry::DriveMax ///< ドライブ最大候補数 + DriveMax = CHostEntry::DriveMax ///< Max number of drive candidates }; private: - // 内部補助用 - void InitOption(const Human68k::argument_t* pArgument); ///< オプション初期化 - BOOL FilesVolume(DWORD nUnit, Human68k::files_t* pFiles); ///< ボリュームラベル取得 + void InitOption(const Human68k::argument_t* pArgument); + BOOL FilesVolume(DWORD nUnit, Human68k::files_t* pFiles); ///< Get volume label - DWORD m_nUnits; ///< 現在のドライブオブジェクト数 (レジューム毎に変化) + DWORD m_nUnits; ///< Number of current drive objects (Changes for every resume) - DWORD m_nOption; ///< 現在の動作フラグ - DWORD m_nOptionDefault; ///< リセット時の動作フラグ + DWORD m_nOption; ///< Current runtime flag + DWORD m_nOptionDefault; ///< Runtime flag at reset - DWORD m_nDrives; ///< ベースパス状態復元用の候補数 (0なら毎回スキャン) + DWORD m_nDrives; ///< Number of candidates for base path status restoration (scan every time if 0) - DWORD m_nKernel; ///< カーネルチェック用カウンタ - DWORD m_nKernelSearch; ///< NULデバイスの先頭アドレス + DWORD m_nKernel; ///< Counter for kernel check + DWORD m_nKernelSearch; ///< Initial address for NUL device - DWORD m_nHostSectorCount; ///< 擬似セクタ番号 + DWORD m_nHostSectorCount; ///< Virtual sector identifier - CHostFilesManager m_cFiles; ///< ファイル検索領域 - CHostFcbManager m_cFcb; ///< FCB操作領域 - CHostEntry m_cEntry; ///< ドライブオブジェクトとディレクトリエントリ + CHostFilesManager m_cFiles; ///< File search memory + CHostFcbManager m_cFcb; ///< FCB operation memory + CHostEntry m_cEntry; ///< Drive object and directory entry DWORD m_nHostSectorBuffer[XM6_HOST_PSEUDO_CLUSTER_MAX]; - ///< 擬似セクタの指すファイル実体 + ///< Entity that the virtual sector points to - DWORD m_nFlag[DriveMax]; ///< ベースパス状態復元用の動作フラグ候補 - TCHAR m_szBase[DriveMax][FILEPATH_MAX]; ///< ベースパス状態復元用の候補 - static DWORD g_nOption; ///< ファイル名変換フラグ + DWORD m_nFlag[DriveMax]; ///< Candidate runtime flag for base path restoration + TCHAR m_szBase[DriveMax][FILEPATH_MAX]; ///< Candidate for base path restoration + static DWORD g_nOption; ///< File name change flag }; diff --git a/src/raspberrypi/fileio.cpp b/src/raspberrypi/fileio.cpp index fd795447..066abddd 100644 --- a/src/raspberrypi/fileio.cpp +++ b/src/raspberrypi/fileio.cpp @@ -99,7 +99,7 @@ BOOL Fileio::Open(const char *fname, OpenMode mode, BOOL directIO) break; case ReadWrite: - // CD-ROMからの読み込みはRWが成功してしまう + // Make sure RW does not succeed when reading from CD-ROM if (access(fname, 0x06) != 0) { return FALSE; } diff --git a/src/web/file_cmds.py b/src/web/file_cmds.py index 5adcbacb..62a9e64d 100644 --- a/src/web/file_cmds.py +++ b/src/web/file_cmds.py @@ -1,10 +1,7 @@ -import fnmatch import os import subprocess import time -import io -import re -import sys +import logging from ractl_cmds import ( attach_image, @@ -14,6 +11,42 @@ from ractl_cmds import ( from settings import * +def list_files(): + from fnmatch import translate + valid_file_types = list(VALID_FILE_SUFFIX) + valid_file_types = ["*." + s for s in valid_file_types] + valid_file_types = r"|".join([translate(x) for x in valid_file_types]) + + from re import match, IGNORECASE + + files_list = [] + for path, dirs, files in os.walk(base_dir): + # Only list valid file types + files = [f for f in files if match(valid_file_types, f, IGNORECASE)] + files_list.extend( + [ + ( + os.path.join(path, file), + "{:,.1f}".format( + os.path.getsize(os.path.join(path, file)) / float(1 << 20), + ), + os.path.getsize(os.path.join(path, file)), + ) + for file in files + ] + ) + return files_list + + +def list_config_files(): + files_list = [] + for root, dirs, files in os.walk(base_dir): + for file in files: + if file.endswith(".json"): + files_list.append(file) + return files_list + + def create_new_image(file_name, type, size): if file_name == "": file_name = "new_image." + str(int(time.time())) + "." + type @@ -42,14 +75,6 @@ def unzip_file(file_name): return True -def rascsi_service(action): - # start/stop/restart - return ( - subprocess.run(["sudo", "/bin/systemctl", action, "rascsi.service"]).returncode - == 0 - ) - - def download_file_to_iso(scsi_id, url): import urllib.request @@ -60,14 +85,19 @@ def download_file_to_iso(scsi_id, url): tmp_full_path = tmp_dir + file_name iso_filename = base_dir + file_name + ".iso" - urllib.request.urlretrieve(url, tmp_full_path) + try: + urllib.request.urlretrieve(url, tmp_full_path) + except: + # TODO: Capture a more descriptive error message + return {"status": False, "msg": "Error loading the URL"} + # iso_filename = make_cd(tmp_full_path, None, None) # not working yet iso_proc = subprocess.run( ["genisoimage", "-hfs", "-o", iso_filename, tmp_full_path], capture_output=True ) if iso_proc.returncode != 0: - return iso_proc - return attach_image(scsi_id, iso_filename, "SCCD") + return {"status": False, "msg": iso_proc} + return attach_image(scsi_id, type="SCCD", image=iso_filename) def download_image(url): @@ -76,50 +106,69 @@ def download_image(url): file_name = url.split("/")[-1] full_path = base_dir + file_name - urllib.request.urlretrieve(url, full_path) - -def write_config_csv(file_name): - import csv - - # This method takes the output of 'rasctl -l' and parses it into csv format: - # 0: ID - # 1: Unit Number (unused in rascsi-web) - # 2: Device Type - # 3: Device Status (includes the path to a loaded image file) - # TODO: Remove the dependence on rasctl; e.g. when implementing protobuf for rascsi-web try: - with open(file_name, "w") as csv_file: - writer = csv.writer(csv_file) - for device in list_devices(): - if device["type"] != "-": - device_info = list (device.values()) - # Match a *nix file path inside column 3, cutting out the last chunk that starts with a space - filesearch = re.search("(^(/[^/ ]*)+)(\s.*)*$", device_info[3]) - if filesearch is None: - device_info[3] = "" - else: - device_info[3] = filesearch.group(1) - writer.writerow(device_info) - return True + urllib.request.urlretrieve(url, full_path) + return {"status": True, "msg": "Downloaded the URL"} except: - print ("Could not open file for writing: ", file_name) - return False - -def read_config_csv(file_name): - import csv + # TODO: Capture a more descriptive error message + return {"status": False, "msg": "Error loading the URL"} + +def write_config(file_name): + from json import dump try: - with open(file_name) as csv_file: + with open(file_name, "w") as json_file: + devices = list_devices()[0] + for device in devices: + # Remove keys that we don't want to store in the file + del device["status"] + del device["file"] + # It's cleaner not to store an empty parameter for every device without media + if device["image"] == "": + device["image"] = None + # RaSCSI product names will be generated on the fly by RaSCSI + if device["vendor"] == "RaSCSI": + device["vendor"] = device["product"] = device["revision"] = None + # A block size of 0 is how RaSCSI indicates N/A for block size + if device["block_size"] == 0: + device["block_size"] = None + # Convert to a data type that can be serialized + device["params"] = list(device["params"]) + dump(devices, json_file, indent=4) + return {"status": True, "msg": f"Successfully wrote to file: {file_name}"} + #TODO: more verbose error handling of file system errors + except: + logging.error(f"Could not write to file: {file_name}") + return {"status": False, "msg": f"Could not write to file: {file_name}"} + + +def read_config(file_name): + from json import load + try: + with open(file_name) as json_file: detach_all() - config_reader = csv.reader(csv_file) - #TODO: Remove hard-coded string sanitation (e.g. after implementing protobuf) - exclude_list = ("X68000 HOST BRIDGE", "DaynaPort SCSI/Link", " (WRITEPROTECT)", "NO MEDIA") - for row in config_reader: - image_name = row[3] - for e in exclude_list: - image_name = image_name.replace(e, "") - attach_image(row[0], image_name, row[2]) - return True + devices = load(json_file) + for row in devices: + process = attach_image(row["id"], device_type=row["device_type"], image=row["image"], unit=int(row["un"]), \ + params=row["params"], vendor=row["vendor"], product=row["product"], \ + revision=row["revision"], block_size=row["block_size"]) + if process["status"] == True: + return {"status": process["status"], "msg": f"Successfully read from file: {file_name}"} + else: + return {"status": process["status"], "msg": process["msg"]} + #TODO: more verbose error handling of file system errors except: - print ("Could not access file: ", file_name) - return False + logging.error(f"Could not read file: {file_name}") + return {"status": False, "msg": f"Could not read file: {file_name}"} + + +def read_device_config(file_name): + from json import load + try: + with open(file_name) as json_file: + conf = load(json_file) + return {"status": True, "msg": f"Read data from file: {file_name}", "conf": conf} + #TODO: more verbose error handling of file system errors + except: + logging.error(f"Could not read file: {file_name}") + return {"status": False, "msg": f"Could not read file: {file_name}"} diff --git a/src/web/pi_cmds.py b/src/web/pi_cmds.py index 17f716cc..f5164091 100644 --- a/src/web/pi_cmds.py +++ b/src/web/pi_cmds.py @@ -29,3 +29,12 @@ def running_version(): .strip() ) return ra_web_version + " " + pi_version + + +def is_bridge_setup(): + from subprocess import run + process = run(["brctl", "show"], capture_output=True) + output = process.stdout.decode("utf-8") + if "rascsi_bridge" in output: + return True + return False \ No newline at end of file diff --git a/src/web/ractl_cmds.py b/src/web/ractl_cmds.py index 92d978aa..8284f13b 100644 --- a/src/web/ractl_cmds.py +++ b/src/web/ractl_cmds.py @@ -1,164 +1,328 @@ -import fnmatch -import subprocess -import re import logging from settings import * +import rascsi_interface_pb2 as proto -valid_file_suffix = ["*.hda", "*.hdn", "*.hdi", "*.nhd", "*.hdf", "*.hds", "*.hdr", "*.iso", "*.cdr", "*.toast", "*.img", "*.zip"] -valid_file_types = r"|".join([fnmatch.translate(x) for x in valid_file_suffix]) +def get_server_info(): + command = proto.PbCommand() + command.operation = proto.PbOperation.SERVER_INFO + + data = send_pb_command(command.SerializeToString()) + result = proto.PbResult() + result.ParseFromString(data) + version = str(result.server_info.major_version) + "." +\ + str(result.server_info.minor_version) + "." +\ + str(result.server_info.patch_version) + log_levels = result.server_info.log_levels + current_log_level = result.server_info.current_log_level + return {"status": result.status, "version": version, "log_levels": log_levels, "current_log_level": current_log_level} + + +def validate_scsi_id(scsi_id): + from re import match + if match("[0-7]", str(scsi_id)) != None: + return {"status": True, "msg": "Valid SCSI ID."} + else: + return {"status": False, "msg": "Invalid SCSI ID. Should be a number between 0-7"} -def is_active(): - process = subprocess.run(["systemctl", "is-active", "rascsi"], capture_output=True) - return process.stdout.decode("utf-8").strip() == "active" - - -def list_files(): - files_list = [] - for path, dirs, files in os.walk(base_dir): - # Only list valid file types - files = [f for f in files if re.match(valid_file_types, f)] - files_list.extend( - [ - ( - os.path.join(path, file), - # TODO: move formatting to template - "{:,.0f}".format( - os.path.getsize(os.path.join(path, file)) / float(1 << 20) - ) - + " MB", - ) - for file in files - ] - ) - return files_list - - -def list_config_files(): - files_list = [] - for root, dirs, files in os.walk(base_dir): - for file in files: - if file.endswith(".csv"): - files_list.append(file) - return files_list - - -def get_valid_scsi_ids(devices, invalid_list): +def get_valid_scsi_ids(devices, invalid_list, occupied_ids): for device in devices: - if device["file"] != "NO MEDIA" and device["file"] != "-": - invalid_list.append(int(device["id"])) + # Make it possible to insert images on top of a + # removable media device currently without an image attached + if "No Media" in device["status"]: + occupied_ids.remove(device["id"]) - valid_list = list(range(8)) - for id in invalid_list: - try: - valid_list.remove(int(id)) - except: - logging.warning("Invalid SCSI id " + str(id)) - valid_list.reverse() - return valid_list + # Combine lists and remove duplicates + invalid_ids = list(set(invalid_list + occupied_ids)) + valid_ids = list(range(8)) + for id in invalid_ids: + valid_ids.remove(int(id)) + valid_ids.reverse() + return valid_ids def get_type(scsi_id): - return list_devices()[int(scsi_id)]["type"] + device = proto.PbDeviceDefinition() + device.id = int(scsi_id) + + command = proto.PbCommand() + command.operation = proto.PbOperation.DEVICE_INFO + command.devices.append(device) + + data = send_pb_command(command.SerializeToString()) + result = proto.PbResult() + result.ParseFromString(data) + # Assuming that only one PbDevice object is present in the response + try: + result_type = proto.PbDeviceType.Name(result.device_info.devices[0].type) + return {"status": result.status, "msg": result.msg, "device_type": result_type} + except: + return {"status": result.status, "msg": result.msg, "device_type": None} -def attach_image(scsi_id, image, image_type): - if image_type == "SCCD" and get_type(scsi_id) == "SCCD": - return insert(scsi_id, image) +def attach_image(scsi_id, **kwargs): + + # Handling the inserting of media into an attached removable type device + currently_attached = get_type(scsi_id)["device_type"] + device_type = kwargs.get("device_type", None) + + if device_type in REMOVABLE_DEVICE_TYPES and currently_attached in REMOVABLE_DEVICE_TYPES: + if currently_attached != device_type: + return {"status": False, "msg": f"Cannot insert an image for {device_type} into a {currently_attached} device."} + else: + return insert(scsi_id, kwargs.get("image", "")) + # Handling attaching a new device else: - return subprocess.run( - ["rasctl", "-c", "attach", "-t", image_type, "-i", scsi_id, "-f", image], - capture_output=True, - ) + devices = proto.PbDeviceDefinition() + devices.id = int(scsi_id) + if "device_type" in kwargs.keys(): + logging.warning(kwargs["device_type"]) + devices.type = proto.PbDeviceType.Value(str(kwargs["device_type"])) + if "unit" in kwargs.keys(): + devices.unit = kwargs["unit"] + if "image" in kwargs.keys(): + if kwargs["image"] not in [None, ""]: + devices.params.append(kwargs["image"]) + if "params" in kwargs.keys(): + for p in kwargs["params"]: + devices.params.append(p) + if "vendor" in kwargs.keys(): + if kwargs["vendor"] not in [None, ""]: + devices.vendor = kwargs["vendor"] + if "product" in kwargs.keys(): + if kwargs["product"] not in [None, ""]: + devices.product = kwargs["product"] + if "revision" in kwargs.keys(): + if kwargs["revision"] not in [None, ""]: + devices.revision = kwargs["revision"] + if "block_size" in kwargs.keys(): + if kwargs["block_size"] not in [None, ""]: + devices.block_size = int(kwargs["block_size"]) + + command = proto.PbCommand() + command.operation = proto.PbOperation.ATTACH + command.devices.append(devices) + + data = send_pb_command(command.SerializeToString()) + result = proto.PbResult() + result.ParseFromString(data) + return {"status": result.status, "msg": result.msg} def detach_by_id(scsi_id): - return subprocess.run(["rasctl", "-c" "detach", "-i", scsi_id], capture_output=True) + devices = proto.PbDeviceDefinition() + devices.id = int(scsi_id) + + command = proto.PbCommand() + command.operation = proto.PbOperation.DETACH + command.devices.append(devices) + + data = send_pb_command(command.SerializeToString()) + result = proto.PbResult() + result.ParseFromString(data) + return {"status": result.status, "msg": result.msg} def detach_all(): - for scsi_id in range(0, 8): - subprocess.run(["rasctl", "-c" "detach", "-i", str(scsi_id)]) + command = proto.PbCommand() + command.operation = proto.PbOperation.DETACH_ALL - -def disconnect_by_id(scsi_id): - return subprocess.run( - ["rasctl", "-c", "disconnect", "-i", scsi_id], capture_output=True - ) + data = send_pb_command(command.SerializeToString()) + result = proto.PbResult() + result.ParseFromString(data) + return {"status": result.status, "msg": result.msg} def eject_by_id(scsi_id): - return subprocess.run(["rasctl", "-i", scsi_id, "-c", "eject"]) + devices = proto.PbDeviceDefinition() + devices.id = int(scsi_id) + + command = proto.PbCommand() + command.operation = proto.PbOperation.EJECT + command.devices.append(devices) + + data = send_pb_command(command.SerializeToString()) + result = proto.PbResult() + result.ParseFromString(data) + return {"status": result.status, "msg": result.msg} def insert(scsi_id, image): - return subprocess.run( - ["rasctl", "-i", scsi_id, "-c", "insert", "-f", image], capture_output=True - ) + devices = proto.PbDeviceDefinition() + devices.id = int(scsi_id) + devices.params.append(image) + + command = proto.PbCommand() + command.operation = proto.PbOperation.INSERT + command.devices.append(devices) + + data = send_pb_command(command.SerializeToString()) + result = proto.PbResult() + result.ParseFromString(data) + return {"status": result.status, "msg": result.msg} def attach_daynaport(scsi_id): - return subprocess.run( - ["rasctl", "-i", scsi_id, "-c", "attach", "-t", "scdp"], - capture_output=True, - ) + devices = proto.PbDeviceDefinition() + devices.id = int(scsi_id) + devices.type = proto.PbDeviceType.SCDP + + command = proto.PbCommand() + command.operation = proto.PbOperation.ATTACH + command.devices.append(devices) + + data = send_pb_command(command.SerializeToString()) + result = proto.PbResult() + result.ParseFromString(data) + return {"status": result.status, "msg": result.msg} -def is_bridge_setup(interface): - process = subprocess.run(["brctl", "show"], capture_output=True) - output = process.stdout.decode("utf-8") - if "rascsi_bridge" in output and interface in output: - return True - return False +def list_devices(scsi_id=None): + from os import path + command = proto.PbCommand() + command.operation = proto.PbOperation.DEVICE_INFO + # If method is called with scsi_id parameter, return the info on those devices + # Otherwise, return the info on all attached devices + if scsi_id != None: + device = proto.PbDeviceDefinition() + device.id = int(scsi_id) + command.devices.append(device) -def daynaport_setup_bridge(interface): - return subprocess.run( - [f"{base_dir}../RASCSI/src/raspberrypi/setup_bridge.sh", interface], - capture_output=True, - ) + data = send_pb_command(command.SerializeToString()) + result = proto.PbResult() + result.ParseFromString(data) - -def rascsi_service(action): - # start/stop/restart - return ( - subprocess.run(["sudo", "/bin/systemctl", action, "rascsi.service"]).returncode - == 0 - ) - - -def list_devices(): device_list = [] + occupied_ids = [] + n = 0 + while n < len(result.device_info.devices): + did = result.device_info.devices[n].id + dun = result.device_info.devices[n].unit + dtype = proto.PbDeviceType.Name(result.device_info.devices[n].type) + dstat = result.device_info.devices[n].status + dprop = result.device_info.devices[n].properties + + # Building the status string + # TODO: This formatting should probably be moved elsewhere + dstat_msg = [] + if dprop.read_only == True: + dstat_msg.append("Read-Only") + if dstat.protected == True and dprop.protectable == True: + dstat_msg.append("Write-Protected") + if dstat.removed == True and dprop.removable == True: + dstat_msg.append("No Media") + if dstat.locked == True and dprop.lockable == True: + dstat_msg.append("Locked") + + dpath = result.device_info.devices[n].file.name + dfile = path.basename(dpath) + dparam = result.device_info.devices[n].params + dven = result.device_info.devices[n].vendor + dprod = result.device_info.devices[n].product + drev = result.device_info.devices[n].revision + dblock = result.device_info.devices[n].block_size + + device_list.append({"id": did, "un": dun, "device_type": dtype, \ + "status": ", ".join(dstat_msg), "image": dpath, "file": dfile, "params": dparam,\ + "vendor": dven, "product": dprod, "revision": drev, "block_size": dblock}) + occupied_ids.append(did) + n += 1 + return device_list, occupied_ids + + +def sort_and_format_devices(device_list, occupied_ids): + # Add padding devices and sort the list for id in range(8): - device_list.append({"id": str(id), "un": "-", "type": "-", "file": "-"}) - output = subprocess.run(["rasctl", "-l"], capture_output=True).stdout.decode( - "utf-8" - ) - for line in output.splitlines(): - # Valid line to process, continue - if ( - not line.startswith("+") - and not line.startswith("| ID |") - and ( - not line.startswith("No device is installed.") - or line.startswith("No images currently attached.") - ) - and len(line) > 0 - ): - line.rstrip() - device = {} - segments = line.split("|") - if len(segments) > 4: - idx = int(segments[1].strip()) - device_list[idx]["id"] = str(idx) - device_list[idx]["un"] = segments[2].strip() - device_list[idx]["type"] = segments[3].strip() - device_list[idx]["file"] = segments[4].strip() + if id not in occupied_ids: + device_list.append({"id": id, "type": "-", \ + "status": "-", "file": "-", "product": "-"}) + # Sort list of devices by id + device_list.sort(key=lambda dic: str(dic["id"])) return device_list + def reserve_scsi_ids(reserved_scsi_ids): - scsi_ids = ",".join(list(reserved_scsi_ids)) - return subprocess.run(["rasctl", "-r", scsi_ids]) + command = proto.PbCommand() + command.operation = proto.PbOperation.RESERVE + command.params.append(reserved_scsi_ids) + + data = send_pb_command(command.SerializeToString()) + result = proto.PbResult() + result.ParseFromString(data) + return {"status": result.status, "msg": result.msg} + + +def set_log_level(log_level): + '''Sends a command to the server to change the log level. Takes target log level as an argument''' + command = proto.PbCommand() + command.operation = proto.PbOperation.LOG_LEVEL + command.params.append(str(log_level)) + + data = send_pb_command(command.SerializeToString()) + result = proto.PbResult() + result.ParseFromString(data) + return {"status": result.status, "msg": result.msg} + + +def send_pb_command(payload): + # Host and port number where rascsi is listening for socket connections + HOST = 'localhost' + PORT = 6868 + + counter = 0 + tries = 100 + error_msg = "" + + import socket + while counter < tries: + try: + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: + s.connect((HOST, PORT)) + return send_over_socket(s, payload) + except socket.error as error: + counter += 1 + logging.warning("The RaSCSI service is not responding - attempt " + \ + str(counter) + "/" + str(tries)) + error_msg = str(error) + + logging.error(error_msg) + + # After failing all attempts, throw a 404 error + from flask import abort + abort(404, "Failed to connect to RaSCSI at " + str(HOST) + ":" + str(PORT) + \ + " with error: " + error_msg + ". Is the RaSCSI service running?") + + +def send_over_socket(s, payload): + from struct import pack, unpack + + # Prepending a little endian 32bit header with the message size + s.send(pack("= 4: + # Extracting the response header to get the length of the response message + response_length = unpack("=3.17.3 diff --git a/src/web/settings.py b/src/web/settings.py index 85036168..a91156e7 100644 --- a/src/web/settings.py +++ b/src/web/settings.py @@ -1,4 +1,14 @@ -import os +from os import getenv -base_dir = os.getenv("BASE_DIR", "/home/pi/images/") -MAX_FILE_SIZE = os.getenv("MAX_FILE_SIZE", 1024 * 1024 * 1024 * 2) # 2gb +base_dir = getenv("BASE_DIR", "/home/pi/images/") +DEFAULT_CONFIG = base_dir + "default.json" +MAX_FILE_SIZE = getenv("MAX_FILE_SIZE", 1024 * 1024 * 1024 * 2) # 2gb + +HARDDRIVE_FILE_SUFFIX = ("hda", "hdn", "hdi", "nhd", "hdf", "hds") +CDROM_FILE_SUFFIX = ("iso", "cdr", "toast", "img") +REMOVABLE_FILE_SUFFIX = ("hdr",) +ARCHIVE_FILE_SUFFIX = ("zip",) +VALID_FILE_SUFFIX = HARDDRIVE_FILE_SUFFIX + REMOVABLE_FILE_SUFFIX + \ + CDROM_FILE_SUFFIX + ARCHIVE_FILE_SUFFIX + +REMOVABLE_DEVICE_TYPES = ("SCCD", "SCRM", "SCMO") diff --git a/src/web/static/style.css b/src/web/static/style.css index 9d5dd136..108a3063 100644 --- a/src/web/static/style.css +++ b/src/web/static/style.css @@ -41,4 +41,9 @@ table, tr, td { color: white; font-size:20px; background-color:green; -} \ No newline at end of file +} + +td.inactive { + text-align:center; + background-color:tan; +} diff --git a/src/web/templates/index.html b/src/web/templates/index.html index fab180a2..e9c35aca 100644 --- a/src/web/templates/index.html +++ b/src/web/templates/index.html @@ -1,7 +1,7 @@ {% extends "base.html" %} {% block header %} -{% if active %} +{% if server_info["status"] == True %} Service Running {% else %} Service Stopped @@ -21,12 +21,11 @@ {% block content %}

Current RaSCSI Configuration

-

The default configuration will be loaded when the Web UI starts up.

@@ -42,39 +41,60 @@

+

The default configuration will be loaded when the Web UI starts up, if available.

+ - + + {% for device in devices %} - {% if device.id not in reserved_scsi_ids %} + {% if device["id"]|string() not in reserved_scsi_ids %} - - - + + + {% if device.vendor == "RaSCSI" %} + + {% else %} + + {% endif %} + {% else %} - - - - + + + + + + {% endif %} {% endfor %} @@ -96,24 +116,27 @@
ID TypeStatus FileActionProductActions
{{device.id}}{{device.type}}{{device.file}} - {% if device.type == "SCCD" and device.file != "NO MEDIA" %} + {{device.device_type}}{{device.status}}{{device.file}}{{device.product}}{{device.vendor}} {{device.product}} + {% if device.device_type in removable_device_types and "No Media" not in device.status %}
+
+ + +
+ {% elif device.device_type in ["-"] %} +
-
{% else %}
+
+ + +
{% endif %}
{{device.id}}-Reserved ID-{{device.id}}Reserved ID
- +
+ + {% if not file[0].lower().endswith(archive_file_suffix) %} + {% endif %}
- {% if file[0].endswith('.zip') or file[0].endswith('.ZIP') %} + {% if file[0].lower().endswith(archive_file_suffix) %}
@@ -124,10 +147,11 @@ {% endfor %}
+

Supported file types: {{valid_file_suffix|string()}}


Attach Ethernet Adapter

-

Emulates a SCSI DaynaPORT Ethernet Adapter. Host drivers required.

+

Emulates a SCSI DaynaPORT Ethernet Adapter. Host drivers required.

@@ -223,13 +245,14 @@ @@ -240,6 +263,47 @@
@@ -146,9 +170,7 @@ {% if bridge_configured %} Bridge is currently configured! {% else %} - - - + Bridge is automatically configured when a network adapter is attached. {% endif %}

+ +

Logging

+ + + + +
+
+ + + + + +
+
+ +
+ +

Server Log Level

+ + + + +
+
+ + + +
+
+ +
+

Raspberry Pi Operations

@@ -261,9 +325,11 @@
+ {% endblock %} {% block footer %} -
{{version}}
-
Logs
+
RaSCSI version: {{server_info["version"]}}
+
Server log level: {{server_info["current_log_level"]}}
+
{{version}}
{% endblock %} diff --git a/src/web/web.py b/src/web/web.py index 23a4f2f0..3cc88fd2 100644 --- a/src/web/web.py +++ b/src/web/web.py @@ -1,35 +1,37 @@ -import io -import re -import sys - from flask import Flask, render_template, request, flash, url_for, redirect, send_file, send_from_directory from file_cmds import ( + list_files, + list_config_files, create_new_image, download_file_to_iso, delete_file, unzip_file, download_image, - write_config_csv, - read_config_csv, + write_config, + read_config, + read_device_config, +) +from pi_cmds import ( + shutdown_pi, + reboot_pi, + running_version, + rascsi_service, + is_bridge_setup, ) -from pi_cmds import shutdown_pi, reboot_pi, running_version, rascsi_service from ractl_cmds import ( attach_image, list_devices, - is_active, - list_files, + sort_and_format_devices, detach_by_id, eject_by_id, get_valid_scsi_ids, attach_daynaport, - is_bridge_setup, - daynaport_setup_bridge, - list_config_files, detach_all, - valid_file_suffix, - valid_file_types, reserve_scsi_ids, + get_server_info, + validate_scsi_id, + set_log_level, ) from settings import * @@ -38,21 +40,28 @@ app = Flask(__name__) @app.route("/") def index(): - devices = list_devices() reserved_scsi_ids = app.config.get("RESERVED_SCSI_IDS") - scsi_ids = get_valid_scsi_ids(devices, list(reserved_scsi_ids)) + unsorted_devices, occupied_ids = list_devices() + devices = sort_and_format_devices(unsorted_devices, occupied_ids) + scsi_ids = get_valid_scsi_ids(devices, list(reserved_scsi_ids), occupied_ids) return render_template( "index.html", - bridge_configured=is_bridge_setup("eth0"), + bridge_configured=is_bridge_setup(), devices=devices, - active=is_active(), files=list_files(), config_files=list_config_files(), base_dir=base_dir, scsi_ids=scsi_ids, - reserved_scsi_ids=reserved_scsi_ids, + reserved_scsi_ids=[reserved_scsi_ids], max_file_size=MAX_FILE_SIZE, version=running_version(), + server_info=get_server_info(), + valid_file_suffix=VALID_FILE_SUFFIX, + removable_device_types=REMOVABLE_DEVICE_TYPES, + harddrive_file_suffix=HARDDRIVE_FILE_SUFFIX, + cdrom_file_suffix=CDROM_FILE_SUFFIX, + removable_file_suffix=REMOVABLE_FILE_SUFFIX, + archive_file_suffix=ARCHIVE_FILE_SUFFIX, ) @app.route('/pwa/') @@ -62,10 +71,15 @@ def send_pwa_files(path): @app.route("/config/save", methods=["POST"]) def config_save(): file_name = request.form.get("name") or "default" - file_name = f"{base_dir}{file_name}.csv" + file_name = f"{base_dir}{file_name}.json" - write_config_csv(file_name) - flash(f"Saved config to {file_name}!") + process = write_config(file_name) + if process["status"] == True: + flash(f"Saved config to {file_name}!") + return redirect(url_for("index")) + else: + flash(f"Failed to saved config to {file_name}!", "error") + flash(f"{process['msg']}", "error") return redirect(url_for("index")) @@ -75,10 +89,12 @@ def config_load(): file_name = f"{base_dir}{file_name}" if "load" in request.form: - if read_config_csv(file_name): + process = read_config(file_name) + if process["status"] == True: flash(f"Loaded config from {file_name}!") else: flash(f"Failed to load {file_name}!", "error") + flash(f"{process['msg']}", "error") elif "delete" in request.form: if delete_file(file_name): flash(f"Deleted config {file_name}!") @@ -88,16 +104,20 @@ def config_load(): return redirect(url_for("index")) -@app.route("/logs") -def logs(): - import subprocess +@app.route("/logs/show", methods=["POST"]) +def show_logs(): + lines = request.form.get("lines") or "200" + scope = request.form.get("scope") or "default" - lines = request.args.get("lines") or "100" - process = subprocess.run(["journalctl", "-n", lines], capture_output=True) + from subprocess import run + if scope != "default": + process = run(["journalctl", "-n", lines, "-u", scope], capture_output=True) + else: + process = run(["journalctl", "-n", lines], capture_output=True) if process.returncode == 0: headers = {"content-type": "text/plain"} - return process.stdout.decode("utf-8"), 200, headers + return process.stdout.decode("utf-8"), int(lines), headers else: flash("Failed to get logs") flash(process.stdout.decode("utf-8"), "stdout") @@ -105,86 +125,112 @@ def logs(): return redirect(url_for("index")) +@app.route("/logs/level", methods=["POST"]) +def log_level(): + level = request.form.get("level") or "info" + + process = set_log_level(level) + if process["status"] == True: + flash(f"Log level set to {level}!") + return redirect(url_for("index")) + else: + flash(f"Failed to set log level to {level}!", "error") + flash(process["msg"], "error") + return redirect(url_for("index")) + + @app.route("/daynaport/attach", methods=["POST"]) def daynaport_attach(): scsi_id = request.form.get("scsi_id") + + validate = validate_scsi_id(scsi_id) + if validate["status"] == False: + flash(validate["msg"], "error") + return redirect(url_for("index")) + process = attach_daynaport(scsi_id) - if process.returncode == 0: + if process["status"] == True: flash(f"Attached DaynaPORT to SCSI id {scsi_id}!") return redirect(url_for("index")) else: flash(f"Failed to attach DaynaPORT to SCSI id {scsi_id}!", "error") - flash(process.stdout.decode("utf-8"), "stdout") - flash(process.stderr.decode("utf-8"), "stderr") - return redirect(url_for("index")) - - -@app.route("/daynaport/setup", methods=["POST"]) -def daynaport_setup(): - # Future use for wifi - interface = request.form.get("interface") or "eth0" - process = daynaport_setup_bridge(interface) - if process.returncode == 0: - flash(f"Configured DaynaPORT bridge on {interface}!") - return redirect(url_for("index")) - else: - flash(f"Failed to configure DaynaPORT bridge on {interface}!", "error") - flash(process.stdout.decode("utf-8"), "stdout") - flash(process.stderr.decode("utf-8"), "stderr") + flash(process["msg"], "error") return redirect(url_for("index")) @app.route("/scsi/attach", methods=["POST"]) def attach(): file_name = request.form.get("file_name") + file_size = request.form.get("file_size") scsi_id = request.form.get("scsi_id") - # Validate image type by suffix - print("file_name", file_name) - print("valid_file_types: ", valid_file_types) - if re.match(valid_file_types, file_name): - if file_name.lower().endswith((".iso", ".cdr", ".toast", ".img")): - image_type = "SCCD" - else: - image_type = "SCHD" - else: - flash(f"Unknown file type. Valid files are: {', '.join(valid_file_suffix)}", "error") + validate = validate_scsi_id(scsi_id) + if validate["status"] == False: + flash(validate["msg"], "error") return redirect(url_for("index")) - # Validate the SCSI ID - if re.match("[0-7]", str(scsi_id)) == None: - flash(f"Invalid SCSI ID. Should be a number between 0-7", "error") - return redirect(url_for("index")) + kwargs = {"image": file_name} - process = attach_image(scsi_id, file_name, image_type) - if process.returncode == 0: + # Attempt to load the device config sidecar file: + # same base path but .rascsi instead of the original suffix. + from pathlib import Path + device_config = Path(base_dir + str(Path(file_name).stem) + ".rascsi") + if device_config.is_file(): + process = read_device_config(device_config) + if process["status"] == False: + flash(process["msg"], "error") + return redirect(url_for("index")) + conf = process["conf"] + conf_file_size = conf["blocks"] * conf["block_size"] + if conf_file_size != 0 and conf_file_size > int(file_size): + flash(f"Failed to attach {file_name} to SCSI id {scsi_id}!", "error") + flash(f"The file size {file_size} bytes needs to be at least {conf_file_size} bytes.", "error") + return redirect(url_for("index")) + kwargs["device_type"] = conf["device_type"] + kwargs["vendor"] = conf["vendor"] + kwargs["product"] = conf["product"] + kwargs["revision"] = conf["revision"] + kwargs["block_size"] = conf["block_size"] + # Validate image type by file name suffix as fallback + elif file_name.lower().endswith(CDROM_FILE_SUFFIX): + kwargs["device_type"] = "SCCD" + elif file_name.lower().endswith(REMOVABLE_FILE_SUFFIX): + kwargs["device_type"] = "SCRM" + elif file_name.lower().endswith(HARDDRIVE_FILE_SUFFIX): + kwargs["device_type"] = "SCHD" + + process = attach_image(scsi_id, **kwargs) + if process["status"] == True: flash(f"Attached {file_name} to SCSI id {scsi_id}!") return redirect(url_for("index")) else: flash(f"Failed to attach {file_name} to SCSI id {scsi_id}!", "error") - flash(process.stdout.decode("utf-8"), "stdout") - flash(process.stderr.decode("utf-8"), "stderr") + flash(process["msg"], "error") return redirect(url_for("index")) @app.route("/scsi/detach_all", methods=["POST"]) def detach_all_devices(): - detach_all() - flash("Detached all SCSI devices!") - return redirect(url_for("index")) + process = detach_all() + if process["status"] == True: + flash("Detached all SCSI devices!") + return redirect(url_for("index")) + else: + flash("Failed to detach all SCSI devices!", "error") + flash(process["msg"], "error") + return redirect(url_for("index")) @app.route("/scsi/detach", methods=["POST"]) def detach(): scsi_id = request.form.get("scsi_id") process = detach_by_id(scsi_id) - if process.returncode == 0: - flash("Detached SCSI id " + scsi_id + "!") + if process["status"] == True: + flash(f"Detached SCSI id {scsi_id}!") return redirect(url_for("index")) else: - flash("Failed to detach SCSI id " + scsi_id + "!", "error") - flash(process.stdout, "stdout") - flash(process.stderr, "stderr") + flash(f"Failed to detach SCSI id {scsi_id}!", "error") + flash(process["msg"], "error") return redirect(url_for("index")) @@ -192,15 +238,35 @@ def detach(): def eject(): scsi_id = request.form.get("scsi_id") process = eject_by_id(scsi_id) - if process.returncode == 0: - flash("Ejected scsi id " + scsi_id + "!") + if process["status"] == True: + flash(f"Ejected scsi id {scsi_id}!") return redirect(url_for("index")) else: - flash("Failed to eject SCSI id " + scsi_id + "!", "error") - flash(process.stdout, "stdout") - flash(process.stderr, "stderr") + flash(f"Failed to eject SCSI id {scsi_id}!", "error") + flash(process["msg"], "error") return redirect(url_for("index")) +@app.route("/scsi/info", methods=["POST"]) +def device_info(): + scsi_id = request.form.get("scsi_id") + # Extracting the 0th dictionary in list index 0 + device = list_devices(scsi_id)[0][0] + if str(device["id"]) == scsi_id: + flash("=== DEVICE INFO ===") + flash(f"SCSI ID: {device['id']}") + flash(f"Unit: {device['un']}") + flash(f"Type: {device['device_type']}") + flash(f"Status: {device['status']}") + flash(f"File: {device['image']}") + flash(f"Parameters: {device['params']}") + flash(f"Vendor: {device['vendor']}") + flash(f"Product: {device['product']}") + flash(f"Revision: {device['revision']}") + flash(f"Block Size: {device['block_size']}") + return redirect(url_for("index")) + else: + flash(f"Failed to get device info for SCSI id {scsi_id}!", "error") + return redirect(url_for("index")) @app.route("/pi/reboot", methods=["POST"]) def restart(): @@ -231,23 +297,27 @@ def download_file(): scsi_id = request.form.get("scsi_id") url = request.form.get("url") process = download_file_to_iso(scsi_id, url) - if process.returncode == 0: - flash("File Downloaded") + if process["status"] == True: + flash(f"File Downloaded and Attached to SCSI id {scsi_id}") + flash(process["msg"]) return redirect(url_for("index")) else: - flash("Failed to download file", "error") - flash(process.stdout, "stdout") - flash(process.stderr, "stderr") + flash(f"Failed to download and attach file {url}", "error") + flash(process["msg"], "error") return redirect(url_for("index")) @app.route("/files/download_image", methods=["POST"]) def download_img(): url = request.form.get("url") - # TODO: error handling - download_image(url) - flash("File Downloaded") - return redirect(url_for("index")) + process = download_image(url) + if process["status"] == True: + flash(f"File Downloaded from {url}") + return redirect(url_for("index")) + else: + flash(f"Failed to download file {url}", "error") + flash(process["msg"], "error") + return redirect(url_for("index")) @app.route("/files/upload/", methods=["POST"]) @@ -256,19 +326,22 @@ def upload_file(filename): flash("No file provided.", "error") return redirect(url_for("index")) - file_path = os.path.join(app.config["UPLOAD_FOLDER"], filename) - if os.path.isfile(file_path): + from os import path + file_path = path.join(app.config["UPLOAD_FOLDER"], filename) + if path.isfile(file_path): flash(f"{filename} already exists.", "error") return redirect(url_for("index")) + from io import DEFAULT_BUFFER_SIZE binary_new_file = "bx" - with open(file_path, binary_new_file, buffering=io.DEFAULT_BUFFER_SIZE) as f: - chunk_size = io.DEFAULT_BUFFER_SIZE + with open(file_path, binary_new_file, buffering=DEFAULT_BUFFER_SIZE) as f: + chunk_size = DEFAULT_BUFFER_SIZE while True: chunk = request.stream.read(chunk_size) if len(chunk) == 0: break f.write(chunk) + # TODO: display an informative success message return redirect(url_for("index", filename=filename)) @@ -322,17 +395,25 @@ if __name__ == "__main__": app.secret_key = "rascsi_is_awesome_insecure_secret_key" app.config["SESSION_TYPE"] = "filesystem" app.config["UPLOAD_FOLDER"] = base_dir - os.makedirs(app.config["UPLOAD_FOLDER"], exist_ok=True) + + from os import makedirs + makedirs(app.config["UPLOAD_FOLDER"], exist_ok=True) app.config["MAX_CONTENT_LENGTH"] = MAX_FILE_SIZE - if len(sys.argv) >= 2: - app.config["RESERVED_SCSI_IDS"] = str(sys.argv[1]) + + from sys import argv + if len(argv) >= 2: + # Reserved ids format is a string of digits such as '017' + app.config["RESERVED_SCSI_IDS"] = str(argv[1]) # Reserve SCSI IDs on the backend side to prevent use reserve_scsi_ids(app.config.get("RESERVED_SCSI_IDS")) else: app.config["RESERVED_SCSI_IDS"] = "" - # Load the configuration in default.cvs, if it exists - read_config_csv(f"{base_dir}default.csv") + # Load the default configuration file, if found + from pathlib import Path + default_config = Path(DEFAULT_CONFIG) + if default_config.is_file(): + read_config(default_config) import bjoern print("Serving rascsi-web...")