diff --git a/README.md b/README.md index 079857e..c1e6f42 100644 --- a/README.md +++ b/README.md @@ -34,8 +34,12 @@ build\Release\ParavisInstaller.exe и защита системного диска Windows. - Разметка через diskpart: FAT32 `PARAVIS` 16 ГБ, FAT `VTOYEFI` 32 МБ, exFAT `DATA` (оставшийся объём). -- Галочка «Не копировать Windows ESD» (по умолчанию снята): если включить, - папка `WindowsESD` копируется, но файлы `*.esd` из неё удаляются с DATA. +- Если рядом с установщиком есть `WindowsESD.pf`, после `Data.pf` он + распаковывается на раздел DATA (статус «Распаковка Windows ESD»). +- Галочка «Не копировать Windows ESD» (по умолчанию снята) пропускает + распаковку `WindowsESD.pf`. Если файла нет, галочка неактивна и + распаковка пропускается; если файл не является WIM — ошибка + «WindowsESD.pf повреждён». - Отключённый диск переводится в онлайн перед разметкой. - Проверка разметки по таблице разделов и меткам томов. - Распаковка DISM: образ 1 → `PARAVIS`, 2 → `VTOYEFI`, 3 → `DATA`, с diff --git a/src/app.h b/src/app.h index 31c366e..459d5c4 100644 --- a/src/app.h +++ b/src/app.h @@ -78,6 +78,8 @@ public: std::wstring sig = L"-"; std::wstring runStamp; std::wstring wimPath; + std::wstring esdPfPath; + bool esdPfAvailable = false; int testDisk = -1; std::wstring workText; std::wstring labelText = L"Выбран накопитель:"; @@ -122,6 +124,7 @@ public: void CheckInstalled(); void Apply(); int ApplyImages(int disk, const std::wstring& wim); + int ApplyWindowsEsd(int disk); bool DismApply(const std::wstring& wim, int index, const std::wstring& dir, const std::wstring& caption); int RunCmd(const std::wstring& commandLine, const std::wstring& caption); diff --git a/src/install.cpp b/src/install.cpp index 38584c0..65ef105 100644 --- a/src/install.cpp +++ b/src/install.cpp @@ -6,7 +6,6 @@ #include #include #include -#include #include #include @@ -214,6 +213,12 @@ void App::Apply() return; } + if (esdPfAvailable && !IsWimFile(esdPfPath)) { + ShowMessage(MB_ICONERROR | MB_OK, L"WindowsESD.pf повреждён", + L"Файл WindowsESD.pf не является WIM-архивом:\r\n" + esdPfPath); + return; + } + std::wstring message = Format(L"Накопитель (%.1f ГБ)\r\n\r\n", static_cast(sizeBytes) / 1073741824.0); @@ -224,9 +229,14 @@ void App::Apply() static_cast(p3) / 1024.0); message += L"Затем на разделы будут распакованы файлы из архива Data.pf\r\n"; message += L"(1 -> PARAVIS, 2 -> VTOYEFI, 3 -> DATA).\r\n\r\n"; - if (noEsd) { - message += L"Файлы *.esd из папки WindowsESD копироваться не будут\r\n"; - message += L"(остальные файлы папки будут скопированы).\r\n\r\n"; + if (esdPfAvailable) { + if (noEsd) { + message += L"WindowsESD.pf распаковываться не будет.\r\n\r\n"; + } else { + message += L"После Data.pf будет распакован WindowsESD.pf.\r\n\r\n"; + } + } else { + message += L"WindowsESD.pf не найден - Windows ESD не будет распакован.\r\n\r\n"; } message += L"ВНИМАНИЕ! ВСЕ ДАННЫЕ НА ЭТОМ ДИСКЕ БУДУТ УНИЧТОЖЕНЫ.\r\n"; message += L"Продолжить?"; @@ -341,6 +351,30 @@ void App::Apply() DebugLog(L"ApplyImages result=%d applied=%d cancel=%d", result, applied, cancel ? 1 : 0); if (result == 1) { + if (!noEsd && esdPfAvailable) { + int esdResult = ApplyWindowsEsd(disk); + if (esdResult == 3) { + SetWork(L"Отменено"); + HideProgress(); + BusySet(false); + HideProgress(); + ShowMessage(MB_ICONINFORMATION | MB_OK, L"Отменено", + L"Операция отменена пользователем.\r\n\r\n" + L"Диск размечен, Windows ESD не распакован."); + return; + } + if (esdResult == 2) { + SetWork(L"Ошибка Windows ESD"); + HideProgress(); + ShowMessage(MB_ICONERROR | MB_OK, L"Ошибка Windows ESD", + L"Не удалось распаковать WindowsESD.pf.\r\n\r\n" + L"Проверьте, что файл не повреждён."); + BusySet(false); + HideProgress(); + return; + } + } + std::wstring first = GetPartitionLetter(disk, 0); DebugLog(L"partition0=%s", first.c_str()); @@ -456,39 +490,6 @@ int App::ApplyImages(int disk, const std::wstring& wim) return 2; } - if (i == 2 && noEsd) { - std::wstring esdDir = dir + L"WindowsESD"; - std::error_code ec; - if (std::filesystem::exists(esdDir, ec)) { - std::filesystem::recursive_directory_iterator it( - esdDir, std::filesystem::directory_options::skip_permission_denied, ec); - const std::filesystem::recursive_directory_iterator end; - while (it != end) { - if (ec) { - DebugLog(L"skip ESD: scan stopped ec=%d", ec.value()); - break; - } - const std::filesystem::directory_entry& entry = *it; - std::error_code fileEc; - if (entry.is_regular_file(fileEc)) { - std::wstring ext = entry.path().extension().wstring(); - std::transform(ext.begin(), ext.end(), ext.begin(), - [](wchar_t c) { - return static_cast(towlower(c)); - }); - if (ext == L".esd") { - std::error_code removeEc; - std::filesystem::remove(entry.path(), removeEc); - DebugLog(L"skip ESD: %s removed=%d ec=%d", - entry.path().c_str(), - removeEc ? 0 : 1, removeEc.value()); - } - } - it.increment(ec); - } - } - } - ++applied; if (cancel) { @@ -499,6 +500,49 @@ int App::ApplyImages(int disk, const std::wstring& wim) return 1; } +int App::ApplyWindowsEsd(int disk) +{ + std::wstring dir; + for (int attempt = 0; attempt < 15; ++attempt) { + dir = GetPartitionLetter(disk, 2); + if (!dir.empty()) { + break; + } + SleepPump(1000); + } + + if (dir.empty()) { + ShowMessage(MB_ICONERROR | MB_OK, L"Ошибка", + L"Не удалось определить букву раздела DATA."); + return 2; + } + if (cancel) { + return 3; + } + + if (!IsWimFile(esdPfPath)) { + ShowMessage(MB_ICONERROR | MB_OK, L"WindowsESD.pf повреждён", + L"Файл WindowsESD.pf не является WIM-архивом:\r\n" + esdPfPath); + return 2; + } + + std::wstring caption = L"Распаковка Windows ESD"; + DebugLog(L"ApplyWindowsEsd %s -> %s", esdPfPath.c_str(), dir.c_str()); + phase = 2; + ShowProgress(0); + SetWork(caption); + + bool ok = DismApply(esdPfPath, 1, dir, caption); + phase = 0; + if (!ok) { + if (cancel) { + return 3; + } + return 2; + } + return 1; +} + bool App::DismApply(const std::wstring& wim, int index, const std::wstring& dir, const std::wstring& caption) { diff --git a/src/main.cpp b/src/main.cpp index ca0ac9a..14af42b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -187,6 +187,10 @@ bool App::Init(HWND window) } CreateFonts(); + esdPfPath = JoinPath(GetExeDir(), L"WindowsESD.pf"); + esdPfAvailable = FileExists(esdPfPath); + DebugLog(L"WindowsESD.pf: %s available=%d", esdPfPath.c_str(), esdPfAvailable ? 1 : 0); + hProgress = CreateWindowExW(0, PROGRESS_CLASSW, nullptr, WS_CHILD | PBS_SMOOTH, 14, 152, 348, 5, @@ -500,13 +504,17 @@ void App::Paint(HDC dc) const int cbX = 16; const int cbY = 114; const int cbSize = 14; - FillRectColor(memory, cbX, cbY, cbSize, cbSize, RGB(255, 255, 255)); + bool checkEnabled = esdPfAvailable && !busy; + COLORREF boxFill = checkEnabled ? RGB(255, 255, 255) : ColorFromHex(0xF2F3F5); + COLORREF boxBorder = checkEnabled ? checkBorder : ColorFromHex(0xD5DAE3); + COLORREF boxText = checkEnabled ? checkFg : ColorFromHex(0xA0A6B1); + FillRectColor(memory, cbX, cbY, cbSize, cbSize, boxFill); RECT cbRect{ cbX, cbY, cbX + cbSize - 1, cbY + cbSize - 1 }; - HBRUSH cbBrush = CreateSolidBrush(checkBorder); + HBRUSH cbBrush = CreateSolidBrush(boxBorder); FrameRect(memory, &cbRect, cbBrush); DeleteObject(cbBrush); - if (noEsd) { - COLORREF markColor = dimmed ? ColorFromHex(0xA0A6B1) : ColorFromHex(0x2563EB); + if (noEsd && esdPfAvailable) { + COLORREF markColor = checkEnabled ? ColorFromHex(0x2563EB) : ColorFromHex(0xA0A6B1); HPEN pen = CreatePen(PS_SOLID, 2, markColor); HGDIOBJ oldPen = SelectObject(memory, pen); MoveToEx(memory, cbX + 3, cbY + 7, nullptr); @@ -516,7 +524,7 @@ void App::Paint(HDC dc) DeleteObject(pen); } DrawTextRect(memory, L"Не копировать Windows ESD", fontText, 36, 112, 300, 18, - checkFg, DT_LEFT | DT_VCENTER); + boxText, DT_LEFT | DT_VCENTER); if (busy) { DrawTextRect(memory, workText, fontBold, 14, 132, 348, 18, @@ -725,7 +733,7 @@ void App::OnPoll() checkDown = true; } else if (checkDown) { checkDown = false; - if (hit == Hit::Check && !leftDown) { + if (hit == Hit::Check && !leftDown && esdPfAvailable) { noEsd = !noEsd; DebugLog(L"checkbox noEsd=%d", noEsd ? 1 : 0); InvalidateAll(); @@ -799,7 +807,7 @@ void App::OnPoll() (hit == Hit::Combo || hit == Hit::Drop || hit == Hit::DropName || hit == Hit::DropSize || hit == Hit::Arrow); comboFrame = comboHover ? ColorFromHex(0x7FA6F0) : ColorFromHex(0xD5DAE3); - checkBorder = (hit == Hit::Check) ? ColorFromHex(0x7FA6F0) : ColorFromHex(0x9AA3B2); + checkBorder = (esdPfAvailable && hit == Hit::Check) ? ColorFromHex(0x7FA6F0) : ColorFromHex(0x9AA3B2); if (!disks.empty() && (hit == Hit::Drop || hit == Hit::Arrow || hit == Hit::DropName || diff --git a/src/util.cpp b/src/util.cpp index d74ccf4..04df05f 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -82,6 +82,29 @@ bool FileExists(const std::wstring& path) return attributes != INVALID_FILE_ATTRIBUTES && (attributes & FILE_ATTRIBUTE_DIRECTORY) == 0; } +bool IsWimFile(const std::wstring& path) +{ + HANDLE file = CreateFileW(path.c_str(), GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, + OPEN_EXISTING, 0, nullptr); + if (file == INVALID_HANDLE_VALUE) { + return false; + } + + unsigned char signature[8] = {}; + DWORD read = 0; + BOOL ok = ReadFile(file, signature, sizeof(signature), &read, nullptr); + CloseHandle(file); + if (!ok || read != sizeof(signature)) { + return false; + } + + return signature[0] == 'M' && signature[1] == 'S' && + signature[2] == 'W' && signature[3] == 'I' && + signature[4] == 'M' && signature[5] == 0 && + signature[6] == 0 && signature[7] == 0; +} + bool IsAdmin() { HANDLE token = nullptr; diff --git a/src/util.h b/src/util.h index b930f1f..9db777d 100644 --- a/src/util.h +++ b/src/util.h @@ -10,6 +10,7 @@ std::wstring GetExeDir(); std::wstring GetRunStamp(); std::wstring JoinPath(const std::wstring& dir, const std::wstring& name); bool FileExists(const std::wstring& path); +bool IsWimFile(const std::wstring& path); bool IsAdmin(); std::string WideToAnsi(const std::wstring& text, UINT codepage = CP_ACP); std::wstring AnsiToWide(const std::string& text, UINT codepage = CP_ACP);