config = $config; $this->projectDir = $projectDir; } /** * 检查是否有任何资源配置 */ public function hasResource(): bool { return !empty($this->config['icon']) || !empty($this->config['version-info']) || !empty($this->config['manifest']); } /** * 获取图标文件的绝对路径 */ public function getIconPath(): ?string { $icon = $this->config['icon'] ?? null; if (empty($icon)) { return null; } return $this->resolvePath($icon); } /** * 获取 manifest 文件的绝对路径 */ public function getManifestPath(): ?string { $manifest = $this->config['manifest'] ?? null; if (empty($manifest)) { return null; } return $this->resolvePath($manifest); } /** * 解析相对/绝对路径为绝对路径 */ private function resolvePath(string $path): string { // 如果是绝对路径,直接使用 if (preg_match('/^[A-Za-z]:\\\\|^\//', $path)) { return $path; } // 相对路径,基于项目目录解析 return $this->projectDir . DIRECTORY_SEPARATOR . $path; } /** * 生成 .rc 资源文件内容 */ public function generate(): string { $content = ''; $content .= '// Generated by TypePHP - Windows Resource File' . PHP_EOL; $content .= '// DO NOT EDIT - This file is auto-generated' . PHP_EOL; $content .= PHP_EOL; // 告诉 rc.exe 此文件使用 UTF-8 编码,避免中文乱码 $content .= '#pragma code_page(65001)' . PHP_EOL; $content .= PHP_EOL; // 包含 Windows 版本信息头文件 $content .= '#include ' . PHP_EOL; $content .= PHP_EOL; // Manifest 资源(Windows 清单文件,如 UAC、DPI 感知等) $manifestPath = $this->getManifestPath(); if ($manifestPath) { $manifestPathRc = str_replace('\\', '/', $manifestPath); $content .= '// Manifest Resource' . PHP_EOL; $content .= 'CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "' . addslashes($manifestPathRc) . '"' . PHP_EOL; $content .= PHP_EOL; } // 图标资源 $iconPath = $this->getIconPath(); if ($iconPath) { // 使用正斜杠,Windows RC 编译器更兼容 $iconPathRc = str_replace('\\', '/', $iconPath); $content .= '// Icon Resource' . PHP_EOL; $content .= 'MAINICON ICON "' . addslashes($iconPathRc) . '"' . PHP_EOL; $content .= PHP_EOL; } // 版本信息 $versionInfo = $this->config['version-info'] ?? []; if (!empty($versionInfo)) { $content .= $this->generateVersionInfo($versionInfo); } return $content; } /** * 生成版本信息块 */ private function generateVersionInfo(array $info): string { $fileVersion = $info['file-version'] ?? '0.0.0.0'; $productVersion = $info['product-version'] ?? $fileVersion; $content = ''; $content .= '// Version Information' . PHP_EOL; $content .= '1 VERSIONINFO' . PHP_EOL; $content .= 'FILEVERSION ' . $this->formatVersionDots($fileVersion) . PHP_EOL; $content .= 'PRODUCTVERSION ' . $this->formatVersionDots($productVersion) . PHP_EOL; $content .= 'FILEFLAGSMASK ' . ($info['file-flags-mask'] ?? '0x3fL') . PHP_EOL; $content .= 'FILEFLAGS ' . ($info['file-flags'] ?? '0x0L') . PHP_EOL; $content .= 'FILEOS ' . ($info['file-os'] ?? 'VOS_NT_WINDOWS32') . PHP_EOL; $content .= 'FILETYPE ' . ($info['file-type'] ?? 'VFT_APP') . PHP_EOL; $content .= 'FILESUBTYPE ' . ($info['file-subtype'] ?? 'VFT2_UNKNOWN') . PHP_EOL; $content .= 'BEGIN' . PHP_EOL; // StringFileInfo 块 $content .= ' BLOCK "StringFileInfo"' . PHP_EOL; $content .= ' BEGIN' . PHP_EOL; // 语言代码页(040904b0 = 英文/UTF-8,配合 #pragma code_page(65001) 正确显示中文) $langCodepage = $info['lang-codepage'] ?? '040904b0'; $content .= ' BLOCK "' . $langCodepage . '"' . PHP_EOL; $content .= ' BEGIN' . PHP_EOL; // 字符串值 $stringFields = [ 'company-name' => 'CompanyName', 'file-description' => 'FileDescription', 'file-version' => 'FileVersion', 'internal-name' => 'InternalName', 'legal-copyright' => 'LegalCopyright', 'legal-trademarks' => 'LegalTrademarks', 'original-filename' => 'OriginalFilename', 'product-name' => 'ProductName', 'product-version' => 'ProductVersion', 'comments' => 'Comments', ]; foreach ($stringFields as $yamlKey => $rcKey) { if (isset($info[$yamlKey])) { $value = $info[$yamlKey]; $content .= ' VALUE "' . $rcKey . '", "' . addslashes($value) . '\\0"' . PHP_EOL; } } // 如果没有设置 FileVersion,从 file-version 字段自动填入 if (!isset($info['file-version-str']) && $fileVersion) { // 已在上面通过 file-version 键处理 } $content .= ' END' . PHP_EOL; $content .= ' END' . PHP_EOL; // VarFileInfo 块 $content .= ' BLOCK "VarFileInfo"' . PHP_EOL; $content .= ' BEGIN' . PHP_EOL; // 0x0409 = English(US),1200 = Unicode(UTF-16) // 配合 StringFileInfo 中的 040904b0 代码页,确保中文在 UTF-8 源文件中正确编码 $content .= ' VALUE "Translation", 0x0409, 1200' . PHP_EOL; $content .= ' END' . PHP_EOL; $content .= 'END' . PHP_EOL; return $content; } /** * 将版本号格式化为逗号分隔的格式(1,0,0,0) * 支持以下输入格式: * - "1.0.0.0" → "1,0,0,0" * - "1,0,0,0" → "1,0,0,0" * - "v1052" → "1052,0,0,0" (去掉 v 前缀) * - "1.0" → "1,0,0,0" */ private function formatVersionDots(string $version): string { // 去掉 v/V 前缀(如 v1052 → 1052) $version = ltrim($version, 'vV'); // 如果已经是逗号分隔格式,直接返回 if (str_contains($version, ',')) { return $version; } // 用点号分隔 $parts = explode('.', $version); // 确保每个部分都是数字(过滤掉非数字字符) foreach ($parts as $i => $part) { $parts[$i] = preg_replace('/[^0-9]/', '', $part) ?: '0'; } // 确保恰好有4个部分 while (count($parts) < 4) { $parts[] = '0'; } return implode(',', array_slice($parts, 0, 4)); } /** * 生成 resource.h 头文件内容(可选,供 C++ 代码引用资源 ID) */ public function generateHeader(): string { $content = ''; $content .= '// Generated by TypePHP - Resource Header' . PHP_EOL; $content .= '// DO NOT EDIT - This file is auto-generated' . PHP_EOL; $content .= PHP_EOL; if ($this->getIconPath()) { $content .= '#define MAINICON 101' . PHP_EOL; } return $content; } }