Files
2026-10-17 21:57:25 +08:00

272 lines
12 KiB
Plaintext

from stdint import *
import stdlib
import string
import memhub
import lib.VFs as VFs
import lib.VLog as VLog
import win32.win32base as wb
# ============================================================
# VDirs - common directory table (auto-built / forced override via --path)
#
# Without --path: keep the auto table (two static entries + per-drive Program Files + desktop).
# With --path: no auto-building; table = the command-line paths (ordered, de-duplicated).
# Other modules (VPe/VScan) only read via DirCount()/DirAt(i).
#
# Win32 self-declared (no repo declaration; x64 calling convention):
# kernel32!GetLogicalDrives / GetDriveTypeA / GetEnvironmentVariableA / SetErrorMode;
# shell32!SHGetKnownFolderPath; ole32!CoTaskMemFree.
# Declared per Win32 API docs (winbase.h / shlobj_core.h / combaseapi.h).
# ============================================================
# Win32: typedef LONG HRESULT (signed 32-bit; negative = failure)
typedef HRESULT = int32
# Win32: typedef const KNOWNFOLDERID *REFKNOWNFOLDERID (points to a 16-byte GUID)
typedef REFKNOWNFOLDERID = rawptr
extern def GetLogicalDrives() -> DWORD
extern def GetDriveTypeA(lpRootPathName: wb.LPCSTR) -> UINT
extern def GetEnvironmentVariableA(lpName: wb.LPCSTR, lpBuffer: CHARPTR, nSize: DWORD) -> DWORD
extern def SetErrorMode(uMode: UINT) -> UINT
extern def SHGetKnownFolderPath(rfid: REFKNOWNFOLDERID, dwFlags: DWORD, hToken: wb.HANDLE,
ppszPath: WCHARPTR*) -> HRESULT
extern def CoTaskMemFree(pv: rawptr)
define SEM_FAILCRITICALERRORS = 0x0001 # SetErrorMode flag (errhandlingapi.h)
define KNOWNFOLDERID_SIZE = 16 # KNOWNFOLDERID / GUID byte size
# winnt.h: FILE_ATTRIBUTE_REPARSE_POINT is used by VScan
# GetDriveTypeA return values (winbase.h)
define DRIVE_NO_ROOT_DIR = 1 # skip: no root path
define DRIVE_REMOVABLE = 2 # probe: removable media (USB/card reader)
define DRIVE_FIXED = 3 # probe: fixed disk
define DRIVE_REMOTE = 4 # probe: network drive
define DRIVE_CDROM = 5 # skip: CD-ROM
# Only NO_ROOT_DIR(1) and CDROM(5) are skipped; all others (incl. REMOVABLE) probe Program Files.
# SetErrorMode in AddFixedDriveProgramFiles suppresses popups/blocking from empty media.
define COMMON_DIR_CAPACITY = 128 # table capacity (inline array element count)
define DRIVE_ROOT_CAP = 8 # "X:\" + NUL buffer
define DESKTOP_SUFFIX_CAP = 9 # r"\Desktop"(8) + NUL(1)
# Injected by App/main.vp
_mbuddy: memhub.MemBuddy*
# Explicit-length inline array + count: avoids t.CArray[str] single-arg form differing
# between TPC (auto-length inline array) and ViperC (pointer).
Common_Directory: [COMMON_DIR_CAPACITY]str
Common_Dir_Count: int = 0 # used slot count
# --path overrides (ordered; collected at parse time, existence checked and warned at build)
Override_Dir: [COMMON_DIR_CAPACITY]str
Override_Mode: int = 0 # 1 = --path seen (force replace)
Override_Count: int = 0
# Table read API (other modules read only through this, never touch Common_Directory)
def DirCount() -> int:
return Common_Dir_Count
def DirAt(Idx: int) -> str:
return Common_Directory[Idx]
def IsOverride() -> int:
return Override_Mode
# Called on first --path (idempotent: clears overrides once); no auto-build afterwards
def BeginOverride():
global Override_Mode, Override_Count
if Override_Mode == 0:
Override_Mode = 1
Override_Count = 0
# Collect one --path override (ordered; existence check deferred to BuildTable)
def AddOverridePath(Path: str) -> int:
global Override_Count
if Path is None:
return 0
if Override_Count >= COMMON_DIR_CAPACITY:
VLog.LogFmtIS(" Warning: --path count reached limit (%d), ignored: %s\n", COMMON_DIR_CAPACITY, Path)
return 0
Override_Dir[Override_Count] = Path
Override_Count += 1
return 1
# Unconditional insert: strcmp de-dup + capacity bound only, no existence check. 1=added, 0=skipped.
# Path must live for the whole process (static literal or MemBuddy pool allocation).
def AddCommonDirForce(Path: str) -> int:
global Common_Dir_Count
if Path is None:
return 0
for i in range(Common_Dir_Count):
if string.strcmp(Common_Directory[i], Path) == 0:
return 0
if Common_Dir_Count >= COMMON_DIR_CAPACITY:
VLog.LogFmt1(" Warning: common dir table full (%d), entry ignored\n", sizet(COMMON_DIR_CAPACITY))
return 0
Common_Directory[Common_Dir_Count] = Path
Common_Dir_Count += 1
return 1
# Add to the common dir table: existence check + de-dup + capacity bound (scanned/derived paths).
# Returns 1=added, 0=skipped (missing / duplicate / table full)
def AddCommonDir(Path: str) -> int:
if Path is None:
return 0
if VFs.PathIsDir(Path) == 0:
return 0
return AddCommonDirForce(Path)
# Enumerate X:\Program Files and X:\Program Files (x86) on each drive; returns added count
def AddFixedDriveProgramFiles() -> int:
# Process-level: suppress critical-error dialogs and make empty drives fail immediately,
# avoiding long blocks. Affects all later file ops for the process lifetime (not auto-reset).
SetErrorMode(SEM_FAILCRITICALERRORS)
Mask: DWORD = GetLogicalDrives()
if Mask == 0:
VLog.LogFmt1(" Warning: GetLogicalDrives failed (win32 err=%u)\n", wb.GetLastError())
return 0
Added: int = 0
for Letter in range(26):
if (Mask & (uint32(1) << uint32(Letter))) != 0:
Root: bytes = _mbuddy.alloc(DRIVE_ROOT_CAP)
if Root is not None:
Root[0] = char(65 + Letter) # 'A' + Letter
Root[1] = ':'
Root[2] = '\\'
Root[3] = 0
Dt: UINT = GetDriveTypeA(Root)
# Only NO_ROOT_DIR(1) and CDROM(5) are skipped; REMOVABLE(2)/FIXED(3)/
# REMOTE(4) still probe Program Files (empty media handled by SetErrorMode above).
if Dt != DRIVE_NO_ROOT_DIR and Dt != DRIVE_CDROM:
Pf: bytes = _mbuddy.alloc(VFs.PATH_BUF_CAP)
if Pf is not None:
string.strcpy(Pf, r"Q:\Program Files") # 'Q' placeholder, rewritten to the drive letter next line
Pf[0] = char(65 + Letter)
Added += AddCommonDir(str(Pf))
Pfx: bytes = _mbuddy.alloc(VFs.PATH_BUF_CAP)
if Pfx is not None:
string.strcpy(Pfx, r"Q:\Program Files (x86)")
Pfx[0] = char(65 + Letter)
Added += AddCommonDir(str(Pfx))
return Added
# Desktop fallback: %USERPROFILE%\Desktop (only when SHGetKnownFolderPath is unavailable/fails).
# Buf is caller-provided (>= PATH_BUF_CAP). Returns 1=added, 0=not added
def AddDesktopFallback(Buf: bytes) -> int:
n: DWORD = GetEnvironmentVariableA("USERPROFILE", Buf, VFs.PATH_BUF_CAP)
# Also append "\Desktop" (8 bytes) + NUL, so n + 9 must not exceed the buffer capacity
if n == 0 or n + uint32(DESKTOP_SUFFIX_CAP) > uint32(VFs.PATH_BUF_CAP):
VLog.LogFmt1(" desktop fallback: USERPROFILE missing or too long (win32 err=%u)\n", wb.GetLastError())
return 0
Buf[n] = 0
string.strcat(Buf, r"\Desktop")
VLog.VerboseFmtS1(" desktop fallback: using %s\n", str(Buf))
if AddCommonDir(str(Buf)) != 0:
return 1
VLog.LogStr(" Warning: desktop fallback path missing or not a directory, not added to common dir table\n")
return 0
# Current user desktop: prefer SHGetKnownFolderPath(FOLDERID_Desktop) (handles OneDrive
# redirection), else fall back explicitly to %USERPROFILE%\Desktop. Returns 1=added, 0=not added
def AddCurrentUserDesktop() -> int:
Buf: bytes = _mbuddy.alloc(VFs.PATH_BUF_CAP)
if Buf is None:
VLog.LogStr(" Warning: desktop path buffer allocation failed\n")
return 0
# FOLDERID_Desktop {B4BFCC3A-DB2C-424C-B029-7FE99A87C641} as 16 raw LE bytes:
# 3A CC BF B4 2C DB 4C 42 B0 29 7F E9 9A 87 C6 41 (wb.GUID mis-models Data4 as a pointer).
GuidBuf: BYTEPTR = stdlib.malloc(KNOWNFOLDERID_SIZE)
if GuidBuf is None:
VLog.LogStr(" Warning: KNOWNFOLDERID buffer allocation failed, using fallback path\n")
return AddDesktopFallback(Buf)
GuidBuf[0] = 0x3A
GuidBuf[1] = 0xCC
GuidBuf[2] = 0xBF
GuidBuf[3] = 0xB4
GuidBuf[4] = 0x2C
GuidBuf[5] = 0xDB
GuidBuf[6] = 0x4C
GuidBuf[7] = 0x42
GuidBuf[8] = 0xB0
GuidBuf[9] = 0x29
GuidBuf[10] = 0x7F
GuidBuf[11] = 0xE9
GuidBuf[12] = 0x9A
GuidBuf[13] = 0x87
GuidBuf[14] = 0xC6
GuidBuf[15] = 0x41
Pwsz: WCHARPTR = None
Hr: HRESULT = SHGetKnownFolderPath(rawptr(GuidBuf), 0, None, &Pwsz)
stdlib.free(rawptr(GuidBuf))
if Hr < 0:
# On failure *ppszPath is NULL by spec; still free a non-NULL return defensively to avoid leaks
if Pwsz is not None:
CoTaskMemFree(rawptr(Pwsz))
VLog.LogFmt1(" desktop: SHGetKnownFolderPath failed (hr=0x%X), using fallback path\n", sizet(uint32(Hr)))
return AddDesktopFallback(Buf)
if Pwsz is None:
VLog.LogStr(" desktop: SHGetKnownFolderPath returned null pointer, using fallback path\n")
return AddDesktopFallback(Buf)
# UTF-16 -> UTF-8: query needed bytes first (incl. NUL; Needed<=0 means failure)
Needed: int = VFs.WideToUtf8Needed(Pwsz)
if Needed <= 0 or Needed > VFs.PATH_BUF_CAP:
CoTaskMemFree(rawptr(Pwsz))
VLog.LogFmt2(" desktop: path empty or too long (%d bytes, limit %d), using fallback path\n",
sizet(uint32(Needed)), sizet(VFs.PATH_BUF_CAP))
return AddDesktopFallback(Buf)
Got: int = VFs.WideToUtf8(Pwsz, Buf, VFs.PATH_BUF_CAP)
CoTaskMemFree(rawptr(Pwsz)) # Free immediately after use: single free point for both branches
if Got == 0:
VLog.LogFmt1(" desktop: UTF-16 to UTF-8 conversion failed (win32 err=%u), using fallback path\n", wb.GetLastError())
return AddDesktopFallback(Buf)
if AddCommonDir(str(Buf)) != 0:
return 1
VLog.LogStr(" Warning: desktop path missing or not a directory, not added to common dir table\n")
return 0
# Print the common dir table (verbose only). Returns 0=non-empty, 1=empty
def PrintCommonDirs() -> int:
if Common_Dir_Count <= 0:
VLog.VerboseStr(" (table empty)\n")
return 1
for i in range(Common_Dir_Count):
VLog.VerboseFmtIS(" [%d] %s\n", sizet(i), Common_Directory[i])
VLog.VerboseFmt1(" total %d common dirs\n", sizet(Common_Dir_Count))
return 0
# Build and print the dir table (call after _mbuddy injection). Returns 0=non-empty, 1=empty.
# Logs distinguish "auto table" / "override table (N)"; missing --paths are warned and skipped.
def BuildTable() -> int:
if Override_Mode == 0:
AddCommonDirForce(r"C:\Viper") # Two static entries: unconditional insert (no existence check)
AddCommonDirForce(r"D:\Viper")
AddFixedDriveProgramFiles() # Per-drive Program Files / Program Files (x86)
AddCurrentUserDesktop() # Current user desktop
VLog.VerboseFmt1(" dir table source: auto table (%u entries)\n", sizet(Common_Dir_Count))
else:
# Force replace: no auto-build; table = --path order (de-dup handled by AddCommonDirForce)
for i in range(Override_Count):
if VFs.PathIsDir(Override_Dir[i]) == 0:
VLog.LogFmtS1(" Warning: --path missing or not a directory, skipped: %s\n", Override_Dir[i])
else:
AddCommonDirForce(Override_Dir[i])
VLog.VerboseFmt1(" dir table source: override table (%u entries)\n", sizet(Common_Dir_Count))
return PrintCommonDirs()