🐧 Cross-platform build support

This commit is contained in:
bitcookies
2026-04-05 22:09:59 +08:00
parent 47fe667eda
commit 8c3814fe8e
12 changed files with 908 additions and 19 deletions
+129
View File
@@ -0,0 +1,129 @@
name: Build All Platforms
on:
workflow_dispatch:
jobs:
# --- Windows builds (MSBuild + vcpkg) ---
windows:
strategy:
matrix:
include:
- platform: x64
artifact: winrar-keygen-x64
- platform: Win32
artifact: winrar-keygen-x86
- platform: ARM64
artifact: winrar-keygen-arm64
name: Windows ${{ matrix.platform }}
runs-on: windows-latest
steps:
- uses: actions/checkout@v6.0.2
- name: Setup vcpkg
uses: lukka/run-vcpkg@v11
with:
vcpkgGitCommitId: 'a34c873a9717a888f58dc05268dea15592c2f0ff'
- name: Setup MSBuild
uses: microsoft/setup-msbuild@v2
- name: Build
run: |
msbuild winrar-keygen.sln /p:Configuration=Release /p:Platform=${{ matrix.platform }} /p:VcpkgEnableManifest=true
- name: Prepare artifact
shell: pwsh
run: |
$src = "bin/${{ matrix.platform }}-Release/winrar-keygen.exe"
Copy-Item $src "${{ matrix.artifact }}.exe"
- name: Upload artifact
uses: actions/upload-artifact@v6.0.0
with:
name: ${{ matrix.artifact }}
path: ${{ matrix.artifact }}.exe
# --- Linux x64 build (CMake) ---
linux-x64:
name: Linux x64
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6.0.2
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y libgmp-dev
- name: Build
run: |
mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Release
cmake --build . --config Release
- name: Prepare artifact
run: |
cp build/winrar-keygen winrar-keygen-linux-x64
chmod +x winrar-keygen-linux-x64
- name: Upload artifact
uses: actions/upload-artifact@v6.0.0
with:
name: winrar-keygen-linux-x64
path: winrar-keygen-linux-x64
# --- Linux ARM64 build (native runner) ---
linux-arm64:
name: Linux ARM64
runs-on: ubuntu-24.04-arm
steps:
- uses: actions/checkout@v6.0.2
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y libgmp-dev
- name: Build
run: |
mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Release
cmake --build . --config Release
- name: Prepare artifact
run: |
cp build/winrar-keygen winrar-keygen-linux-arm64
chmod +x winrar-keygen-linux-arm64
- name: Upload artifact
uses: actions/upload-artifact@v6.0.0
with:
name: winrar-keygen-linux-arm64
path: winrar-keygen-linux-arm64
# --- macOS ARM64 build (CMake) ---
macos:
name: macOS ARM64
runs-on: macos-latest
steps:
- uses: actions/checkout@v6.0.2
- name: Install dependencies
run: brew install gmp
- name: Build
run: |
mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Release
cmake --build . --config Release
- name: Prepare artifact
run: |
cp build/winrar-keygen winrar-keygen-macos-arm64
chmod +x winrar-keygen-macos-arm64
- name: Upload artifact
uses: actions/upload-artifact@v6.0.0
with:
name: winrar-keygen-macos-arm64
path: winrar-keygen-macos-arm64
+19 -2
View File
@@ -1,7 +1,24 @@
#pragma once
#include <stddef.h>
#include <stdint.h>
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable: 4146 4244 4267)
#endif
#include <gmp.h>
#ifdef _MSC_VER
#pragma warning(pop)
#endif
// MPIR (used by vcpkg x86/x64) provides mpz_*_sx/mpz_*_ux for intmax_t.
// Standard GMP (used by ARM64 and Linux/macOS) does not; fall back to long variants.
#ifndef __MPIR_VERSION
#define mpz_init_set_sx(z, v) mpz_init_set_si((z), static_cast<long>(v))
#define mpz_init_set_ux(z, v) mpz_init_set_ui((z), static_cast<unsigned long>(v))
#define mpz_set_sx(z, v) mpz_set_si((z), static_cast<long>(v))
#define mpz_set_ux(z, v) mpz_set_ui((z), static_cast<unsigned long>(v))
#endif
#include <vector>
#include <string>
#include <type_traits>
@@ -300,11 +317,11 @@ public:
}
bool TestBit(size_t i) const noexcept {
return mpz_tstbit(_Value, i) != 0;
return mpz_tstbit(_Value, static_cast<mp_bitcnt_t>(i)) != 0;
}
void SetBit(size_t i) noexcept {
mpz_setbit(_Value, i);
mpz_setbit(_Value, static_cast<mp_bitcnt_t>(i));
}
std::string ToString(size_t Base, bool LowerCase) const {
+50
View File
@@ -0,0 +1,50 @@
cmake_minimum_required(VERSION 3.16)
project(winrar-keygen LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# Version
set(APP_VERSION "4.1.0.0")
add_executable(winrar-keygen
_tmain.cpp
BigInteger.hpp
EllipticCurveGF2m.hpp
GaloisField.hpp
Hasher.hpp
HasherCrc32Traits.hpp
HasherSha1Traits.hpp
WinRarConfig.hpp
WinRarKeygen.hpp
)
target_compile_definitions(winrar-keygen PRIVATE
APP_VERSION="${APP_VERSION}"
)
# Find GMP
find_path(GMP_INCLUDE_DIR gmp.h REQUIRED)
target_include_directories(winrar-keygen PRIVATE ${GMP_INCLUDE_DIR})
# macOS Universal Binary support
if(APPLE AND CMAKE_OSX_ARCHITECTURES)
message(STATUS "Building macOS Universal Binary: ${CMAKE_OSX_ARCHITECTURES}")
endif()
# Static linking on Linux for portable binaries
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
find_library(GMP_STATIC_LIBRARY libgmp.a)
if(GMP_STATIC_LIBRARY)
target_link_libraries(winrar-keygen PRIVATE ${GMP_STATIC_LIBRARY})
target_link_options(winrar-keygen PRIVATE -static-libgcc -static-libstdc++)
message(STATUS "Using static GMP: ${GMP_STATIC_LIBRARY}")
else()
find_library(GMP_LIBRARY gmp REQUIRED)
target_link_libraries(winrar-keygen PRIVATE ${GMP_LIBRARY})
message(STATUS "Using dynamic GMP: ${GMP_LIBRARY}")
endif()
else()
find_library(GMP_LIBRARY gmp REQUIRED)
target_link_libraries(winrar-keygen PRIVATE ${GMP_LIBRARY})
endif()
+135 -1
View File
@@ -149,5 +149,139 @@ public:
}
}
};
#endif
#else
#include <cstring>
#include <stdexcept>
struct HasherSha1Traits {
public:
static constexpr size_t BlockSize = 512 / 8;
static constexpr size_t DigestSize = 160 / 8;
struct DigestType {
uint8_t Bytes[DigestSize];
};
struct ContextType {
uint32_t State[5];
uint64_t Count;
uint8_t Buffer[64];
ContextType() noexcept : State{}, Count(0), Buffer{} {}
ContextType(const ContextType&) noexcept = default;
ContextType& operator=(const ContextType&) noexcept = default;
ContextType(ContextType&& Other) noexcept = default;
ContextType& operator=(ContextType&& Other) noexcept = default;
};
private:
static inline uint32_t RotateLeft(uint32_t x, int n) noexcept {
return (x << n) | (x >> (32 - n));
}
static void ProcessBlock(uint32_t state[5], const uint8_t block[64]) noexcept {
uint32_t w[80];
for (int i = 0; i < 16; ++i) {
w[i] = (static_cast<uint32_t>(block[i * 4]) << 24) |
(static_cast<uint32_t>(block[i * 4 + 1]) << 16) |
(static_cast<uint32_t>(block[i * 4 + 2]) << 8) |
(static_cast<uint32_t>(block[i * 4 + 3]));
}
for (int i = 16; i < 80; ++i) {
w[i] = RotateLeft(w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16], 1);
}
uint32_t a = state[0], b = state[1], c = state[2], d = state[3], e = state[4];
for (int i = 0; i < 80; ++i) {
uint32_t f, k;
if (i < 20) { f = (b & c) | (~b & d); k = 0x5A827999; }
else if (i < 40) { f = b ^ c ^ d; k = 0x6ED9EBA1; }
else if (i < 60) { f = (b & c) | (b & d) | (c & d); k = 0x8F1BBCDC; }
else { f = b ^ c ^ d; k = 0xCA62C1D6; }
uint32_t temp = RotateLeft(a, 5) + f + e + k + w[i];
e = d; d = c; c = RotateLeft(b, 30); b = a; a = temp;
}
state[0] += a; state[1] += b; state[2] += c; state[3] += d; state[4] += e;
}
public:
static inline ContextType ContextCreate() {
ContextType Ctx;
Ctx.State[0] = 0x67452301;
Ctx.State[1] = 0xEFCDAB89;
Ctx.State[2] = 0x98BADCFE;
Ctx.State[3] = 0x10325476;
Ctx.State[4] = 0xC3D2E1F0;
Ctx.Count = 0;
return Ctx;
}
static inline ContextType ContextCreate(const void* lpBuffer, size_t cbBuffer) {
ContextType Ctx = ContextCreate();
ContextUpdate(Ctx, lpBuffer, cbBuffer);
return Ctx;
}
static inline ContextType ContextCopy(const ContextType& Ctx) {
return Ctx;
}
static inline void ContextUpdate(ContextType& Ctx, const void* lpBuffer, size_t cbBuffer) {
auto data = reinterpret_cast<const uint8_t*>(lpBuffer);
size_t bufferOffset = static_cast<size_t>(Ctx.Count % 64);
Ctx.Count += cbBuffer;
if (bufferOffset > 0) {
size_t toCopy = 64 - bufferOffset;
if (toCopy > cbBuffer) toCopy = cbBuffer;
std::memcpy(Ctx.Buffer + bufferOffset, data, toCopy);
data += toCopy;
cbBuffer -= toCopy;
bufferOffset += toCopy;
if (bufferOffset == 64) {
ProcessBlock(Ctx.State, Ctx.Buffer);
bufferOffset = 0;
}
}
while (cbBuffer >= 64) {
ProcessBlock(Ctx.State, data);
data += 64;
cbBuffer -= 64;
}
if (cbBuffer > 0) {
std::memcpy(Ctx.Buffer, data, cbBuffer);
}
}
static inline void ContextEvaluate(const ContextType& Ctx, DigestType& Digest) {
ContextType tmp = Ctx;
uint64_t totalBits = tmp.Count * 8;
uint8_t pad = 0x80;
ContextUpdate(tmp, &pad, 1);
uint8_t zero = 0;
while (tmp.Count % 64 != 56) {
ContextUpdate(tmp, &zero, 1);
}
uint8_t lenBytes[8];
for (int i = 7; i >= 0; --i) {
lenBytes[i] = static_cast<uint8_t>(totalBits);
totalBits >>= 8;
}
ContextUpdate(tmp, lenBytes, 8);
for (int i = 0; i < 5; ++i) {
Digest.Bytes[i * 4] = static_cast<uint8_t>(tmp.State[i] >> 24);
Digest.Bytes[i * 4 + 1] = static_cast<uint8_t>(tmp.State[i] >> 16);
Digest.Bytes[i * 4 + 2] = static_cast<uint8_t>(tmp.State[i] >> 8);
Digest.Bytes[i * 4 + 3] = static_cast<uint8_t>(tmp.State[i]);
}
}
static inline void ContextDestroy(ContextType& Ctx) noexcept {
std::memset(&Ctx, 0, sizeof(Ctx));
}
};
#endif
+9 -3
View File
@@ -9,6 +9,12 @@
#include <string>
#include <utility>
#ifdef _MSC_VER
#define BSWAP32(x) _byteswap_ulong(x)
#else
#define BSWAP32(x) __builtin_bswap32(x)
#endif
template<typename __ConfigType>
class WinRarKeygen {
public:
@@ -40,7 +46,7 @@ private:
Sha1Digest = Sha1.Evaluate();
for (unsigned i = 0; i < 5; ++i) {
Generator[i + 1] = _byteswap_ulong(reinterpret_cast<uint32_t*>(Sha1Digest.Bytes)[i]);
Generator[i + 1] = BSWAP32(reinterpret_cast<uint32_t*>(Sha1Digest.Bytes)[i]);
}
} else {
Generator[1] = 0xeb3eb781;
@@ -59,7 +65,7 @@ private:
Sha1Digest = Sha1.Evaluate();
RawPrivateKey[i] = static_cast<uint16_t>(
_byteswap_ulong(reinterpret_cast<uint32_t*>(Sha1Digest.Bytes)[0])
BSWAP32(reinterpret_cast<uint32_t*>(Sha1Digest.Bytes)[0])
);
}
@@ -112,7 +118,7 @@ private:
HasherSha1Traits::DigestType Sha1Digest = Sha1.Evaluate();
for (size_t i = 0; i < 5; ++i) {
RawHash[i] = _byteswap_ulong(reinterpret_cast<uint32_t*>(Sha1Digest.Bytes)[i]);
RawHash[i] = BSWAP32(reinterpret_cast<uint32_t*>(Sha1Digest.Bytes)[i]);
}
// SHA1("") with all-zeroed initial value
+470 -7
View File
@@ -1,17 +1,35 @@
#ifdef _WIN32
#include <windows.h>
#include <winhttp.h>
#include <fcntl.h>
#include <io.h>
#else
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <sys/stat.h>
#include <unistd.h>
#include <strings.h>
#endif
#include <locale.h>
#include <iostream>
#include <string>
#include <vector>
#include <codecvt>
#include "WinRarConfig.hpp"
#include "WinRarKeygen.hpp"
#include <system_error>
#include <stdexcept>
#ifdef _WIN32
#pragma comment(lib, "Version.lib")
#pragma comment(lib, "winhttp.lib")
#endif
#ifndef APP_VERSION
#define APP_VERSION "4.1.0.0"
#endif
#ifdef _WIN32
std::string WideToUtf8(const std::wstring& wstr) {
if (wstr.empty()) return {};
int size = WideCharToMultiByte(CP_UTF8, 0, wstr.data(), (int)wstr.size(), nullptr, 0, nullptr, nullptr);
@@ -43,20 +61,30 @@ std::string WideToAnsi(const std::wstring& wstr) {
&result[0], size, nullptr, nullptr);
return result;
}
#endif
enum class Encoding { ASCII, ANSI, UTF8 };
struct Options {
#ifdef _WIN32
std::wstring username;
std::wstring license;
Encoding encoding = Encoding::UTF8;
std::wstring outputFile = L"rarreg.key";
#else
std::string username;
std::string license;
Encoding encoding = Encoding::UTF8;
std::string outputFile = "rarreg.key";
#endif
bool textOnly = false;
bool activate = false;
bool showVersion = false;
bool showHelp = false;
bool checkUpdate = false;
};
#ifdef _WIN32
bool ParseArguments(int argc, wchar_t* argv[], Options& opts) {
std::vector<std::wstring> positional;
for (int i = 1; i < argc; ++i) {
@@ -73,6 +101,9 @@ bool ParseArguments(int argc, wchar_t* argv[], Options& opts) {
if (arg == L"-a" || arg == L"--activate") {
opts.activate = true; continue;
}
if (arg == L"-u" || arg == L"--update") {
opts.checkUpdate = true; return true;
}
if (arg == L"-e" || arg == L"--encoding") {
if (++i >= argc) {
std::wcerr << L"Error: Missing value for " << arg << L"\n";
@@ -114,7 +145,70 @@ bool ParseArguments(int argc, wchar_t* argv[], Options& opts) {
<< positional.size() << L"\n";
return false;
}
#else
bool ParseArguments(int argc, char* argv[], Options& opts) {
std::vector<std::string> positional;
for (int i = 1; i < argc; ++i) {
std::string arg = argv[i];
if (arg == "-v" || arg == "--version" || strcasecmp(arg.c_str(), "ver") == 0) {
opts.showVersion = true; return true;
}
if (arg == "-h" || arg == "--help" || strcasecmp(arg.c_str(), "help") == 0) {
opts.showHelp = true; return true;
}
if (arg == "-t" || arg == "--text") {
opts.textOnly = true; continue;
}
if (arg == "-a" || arg == "--activate") {
opts.activate = true; continue;
}
if (arg == "-u" || arg == "--update") {
opts.checkUpdate = true; return true;
}
if (arg == "-e" || arg == "--encoding") {
if (++i >= argc) {
std::cerr << "Error: Missing value for " << arg << "\n";
return false;
}
std::string val = argv[i];
if (strcasecmp(val.c_str(), "ascii") == 0) opts.encoding = Encoding::ASCII;
else if (strcasecmp(val.c_str(), "ansi") == 0) opts.encoding = Encoding::ANSI;
else if (strcasecmp(val.c_str(), "utf8") == 0 || strcasecmp(val.c_str(), "utf-8") == 0)
opts.encoding = Encoding::UTF8;
else {
std::cerr << "Error: Unknown encoding '" << val << "'. Use: ascii, ansi, utf8\n";
return false;
}
continue;
}
if (arg == "-o" || arg == "--output") {
if (++i >= argc) {
std::cerr << "Error: Missing value for " << arg << "\n";
return false;
}
opts.outputFile = argv[i]; continue;
}
if (!arg.empty() && arg[0] == '-') {
std::cerr << "Error: Unknown option '" << arg << "'\n";
return false;
}
positional.push_back(arg);
}
if (positional.size() == 2) {
opts.username = positional[0];
opts.license = positional[1];
return true;
}
if (positional.empty()) {
opts.showHelp = true; return true;
}
std::cerr << "Error: Expected 2 arguments (Username, LicenseName), got "
<< positional.size() << "\n";
return false;
}
#endif
#ifdef _WIN32
void ShowHelp(const std::wstring& version) {
std::wcout << L"WinRAR Keygen v" << version << L"\n\n";
std::wcout << L"Usage:\n";
@@ -126,17 +220,43 @@ void ShowHelp(const std::wstring& version) {
std::wcout << L" -o, --output <file> Output file (default: rarreg.key)\n";
std::wcout << L" -a, --activate Write to %APPDATA%\\WinRAR\\rarreg.key\n";
std::wcout << L" -t, --text Print to console only, don't write file\n";
std::wcout << L" -u, --update Check for updates on GitHub\n";
std::wcout << L" -v, --version Show version\n";
std::wcout << L" -h, --help Show this help\n\n";
std::wcout << L"Examples:\n";
std::wcout << L" winrar-keygen.exe \"Github\" \"Single PC usage license\"\n";
std::wcout << L" winrar-keygen.exe \"Github\" \"Single PC usage license\" -e ansi\n";
std::wcout << L" winrar-keygen.exe \"YourName\" \"Single PC usage license\" -e utf8\n";
std::wcout << L" winrar-keygen.exe \"Github\" \"Single PC usage license\" -o \"D:\\keys\\rarreg.key\"\n";
std::wcout << L" winrar-keygen.exe \"Github\" \"Single PC usage license\" --activate\n";
std::wcout << L" winrar-keygen.exe \"Github\" \"Single PC usage license\" -e ascii\n";
std::wcout << L" winrar-keygen.exe \"Github\" \"Single PC usage license\" -a\n";
std::wcout << L" winrar-keygen.exe \"Github\" \"Single PC usage license\" -t\n";
}
#else
void ShowHelp(const std::string& version) {
std::cout << "WinRAR Keygen v" << version << "\n\n";
std::cout << "Usage:\n";
std::cout << " winrar-keygen <Username> <LicenseName> [options]\n";
std::cout << " winrar-keygen -v | --version\n";
std::cout << " winrar-keygen -h | --help\n\n";
std::cout << "Options:\n";
std::cout << " -e, --encoding <enc> utf8 (default), ascii\n";
std::cout << " -o, --output <file> Output file (default: rarreg.key)\n";
#ifdef __APPLE__
std::cout << " -a, --activate Write to ~/Library/Application Support/com.rarlab.WinRAR/rarreg.key\n";
#else
std::cout << " -a, --activate Write to ~/.rarkey\n";
#endif
std::cout << " -t, --text Print to console only, don't write file\n";
std::cout << " -u, --update Check for updates on GitHub\n";
std::cout << " -v, --version Show version\n";
std::cout << " -h, --help Show this help\n\n";
std::cout << "Examples:\n";
std::cout << " winrar-keygen \"Github\" \"Single PC usage license\"\n";
std::cout << " winrar-keygen \"Github\" \"Single PC usage license\" -e ascii\n";
std::cout << " winrar-keygen \"Github\" \"Single PC usage license\" -a\n";
std::cout << " winrar-keygen \"Github\" \"Single PC usage license\" -t\n";
}
#endif
#ifdef _WIN32
std::wstring GetExecutableVersion() {
wchar_t exePath[MAX_PATH] = {};
DWORD pathLen = GetModuleFileNameW(nullptr, exePath, MAX_PATH);
@@ -169,7 +289,13 @@ std::wstring GetExecutableVersion() {
std::to_wstring(HIWORD(fixedInfo->dwFileVersionLS)) + L"." +
std::to_wstring(LOWORD(fixedInfo->dwFileVersionLS));
}
#else
std::string GetAppVersion() {
return APP_VERSION;
}
#endif
#ifdef _WIN32
void PrintRegisterInfo(const WinRarKeygen<WinRarConfig>::RegisterInfo& Info,
const std::wstring& wUser, const std::wstring& wLicense) {
std::wstring uid = Utf8ToWide(Info.UID);
@@ -184,6 +310,19 @@ void PrintRegisterInfo(const WinRarKeygen<WinRarConfig>::RegisterInfo& Info,
std::wcout << data.substr(i, 54) << L"\n";
}
}
#else
void PrintRegisterInfo(const WinRarKeygen<WinRarConfig>::RegisterInfo& Info,
const std::string& user, const std::string& license) {
std::cout << "RAR registration data\n";
std::cout << user << "\n";
std::cout << license << "\n";
std::cout << "UID=" << Info.UID << "\n";
for (size_t i = 0; i < Info.HexData.length(); i += 54) {
std::cout << Info.HexData.substr(i, 54) << "\n";
}
}
#endif
std::string BuildRegFileContent(const WinRarKeygen<WinRarConfig>::RegisterInfo& Info) {
std::string s;
@@ -197,6 +336,7 @@ std::string BuildRegFileContent(const WinRarKeygen<WinRarConfig>::RegisterInfo&
return s;
}
#ifdef _WIN32
bool WriteRegFile(const std::wstring& filePath, const std::string& content) {
FILE* fp = nullptr;
if (_wfopen_s(&fp, filePath.c_str(), L"wb") != 0 || !fp)
@@ -205,7 +345,17 @@ bool WriteRegFile(const std::wstring& filePath, const std::string& content) {
fclose(fp);
return written == content.size();
}
#else
bool WriteRegFile(const std::string& filePath, const std::string& content) {
FILE* fp = fopen(filePath.c_str(), "wb");
if (!fp) return false;
size_t written = fwrite(content.data(), 1, content.size(), fp);
fclose(fp);
return written == content.size();
}
#endif
#ifdef _WIN32
bool IsConsoleHandle(HANDLE handle) {
if (handle == nullptr || handle == INVALID_HANDLE_VALUE) {
return false;
@@ -238,7 +388,187 @@ void ConfigureConsoleOutput() {
std::wcerr << L"Failed to set stderr _O_U8TEXT\n";
}
}
#endif
struct Version {
int major = 0, minor = 0, patch = 0;
bool valid = false;
};
Version ParseVersion(const std::string& verStr) {
Version v;
std::string s = verStr;
if (!s.empty() && (s[0] == 'v' || s[0] == 'V')) s = s.substr(1);
#ifdef _MSC_VER
int count = sscanf_s(s.c_str(), "%d.%d.%d", &v.major, &v.minor, &v.patch);
#else
int count = sscanf(s.c_str(), "%d.%d.%d", &v.major, &v.minor, &v.patch);
#endif
v.valid = (count >= 2);
return v;
}
bool IsNewer(const Version& remote, const Version& local) {
if (remote.major != local.major) return remote.major > local.major;
if (remote.minor != local.minor) return remote.minor > local.minor;
return remote.patch > local.patch;
}
std::string ExtractTagFromJson(const std::string& responseBody) {
std::string tagKey = "\"tag_name\"";
size_t pos = responseBody.find(tagKey);
if (pos == std::string::npos) return "";
pos = responseBody.find('\"', pos + tagKey.length());
if (pos == std::string::npos) return "";
size_t end = responseBody.find('\"', pos + 1);
if (end == std::string::npos) return "";
return responseBody.substr(pos + 1, end - pos - 1);
}
int CompareAndPrintUpdate(const std::string& currentVersion, const std::string& remoteTag) {
Version local = ParseVersion(currentVersion);
Version remote = ParseVersion(remoteTag);
if (!remote.valid) {
#ifdef _WIN32
std::wcerr << L"Error: Could not parse remote version '" << Utf8ToWide(remoteTag) << L"'.\n";
#else
std::cerr << "Error: Could not parse remote version '" << remoteTag << "'.\n";
#endif
return -1;
}
if (IsNewer(remote, local)) {
#ifdef _WIN32
std::wcout << L"\n New version available: " << Utf8ToWide(remoteTag)
<< L" (current: v" << Utf8ToWide(currentVersion) << L")\n";
std::wcout << L" Download: https://github.com/bitcookies/winrar-keygen/releases/latest\n\n";
#else
std::cout << "\n New version available: " << remoteTag
<< " (current: v" << currentVersion << ")\n";
std::cout << " Download: https://github.com/bitcookies/winrar-keygen/releases/latest\n\n";
#endif
} else {
#ifdef _WIN32
std::wcout << L"Already up to date. (v" << Utf8ToWide(currentVersion) << L")\n";
#else
std::cout << "Already up to date. (v" << currentVersion << ")\n";
#endif
}
return 0;
}
#ifdef _WIN32
int CheckForUpdate(const std::string& currentVersion) {
std::wcout << L"Checking for updates...\n";
HINTERNET hSession = WinHttpOpen(L"winrar-keygen-updater",
WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
if (!hSession) {
std::wcerr << L"Error: Failed to initialize WinHTTP.\n";
return -1;
}
HINTERNET hConnect = WinHttpConnect(hSession, L"api.github.com",
INTERNET_DEFAULT_HTTPS_PORT, 0);
if (!hConnect) {
std::wcerr << L"Error: Failed to connect to api.github.com.\n";
WinHttpCloseHandle(hSession);
return -1;
}
HINTERNET hRequest = WinHttpOpenRequest(hConnect, L"GET",
L"/repos/bitcookies/winrar-keygen/releases/latest",
nullptr, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, WINHTTP_FLAG_SECURE);
if (!hRequest) {
std::wcerr << L"Error: Failed to create HTTP request.\n";
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return -1;
}
BOOL bResult = WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0,
WINHTTP_NO_REQUEST_DATA, 0, 0, 0);
if (!bResult) {
std::wcerr << L"Error: Failed to send HTTP request. Check your network connection.\n";
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return -1;
}
bResult = WinHttpReceiveResponse(hRequest, nullptr);
if (!bResult) {
std::wcerr << L"Error: No response from GitHub API.\n";
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return -1;
}
std::string responseBody;
DWORD dwSize = 0;
DWORD dwDownloaded = 0;
do {
dwSize = 0;
WinHttpQueryDataAvailable(hRequest, &dwSize);
if (dwSize == 0) break;
std::vector<char> buffer(dwSize);
WinHttpReadData(hRequest, buffer.data(), dwSize, &dwDownloaded);
responseBody.append(buffer.data(), dwDownloaded);
} while (dwSize > 0);
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
if (responseBody.empty()) {
std::wcerr << L"Error: Empty response from GitHub API.\n";
return -1;
}
std::string remoteTag = ExtractTagFromJson(responseBody);
if (remoteTag.empty()) {
std::wcerr << L"Error: Could not find version info in GitHub response.\n";
return -1;
}
return CompareAndPrintUpdate(currentVersion, remoteTag);
}
#else
int CheckForUpdate(const std::string& currentVersion) {
std::cout << "Checking for updates...\n";
FILE* pipe = popen("curl -s https://api.github.com/repos/bitcookies/winrar-keygen/releases/latest 2>/dev/null", "r");
if (!pipe) {
std::cerr << "Error: Failed to run curl. Make sure curl is installed.\n";
return -1;
}
std::string responseBody;
char buf[4096];
while (fgets(buf, sizeof(buf), pipe)) {
responseBody += buf;
}
int status = pclose(pipe);
if (status != 0 || responseBody.empty()) {
std::cerr << "Error: Failed to fetch update info. Check your network connection.\n";
return -1;
}
std::string remoteTag = ExtractTagFromJson(responseBody);
if (remoteTag.empty()) {
std::cerr << "Error: Could not find version info in GitHub response.\n";
return -1;
}
return CompareAndPrintUpdate(currentVersion, remoteTag);
}
#endif
#ifdef _WIN32
int wmain(int argc, wchar_t* argv[]) {
ConfigureConsoleOutput();
@@ -248,6 +578,7 @@ int wmain(int argc, wchar_t* argv[]) {
}
std::wstring version = GetExecutableVersion();
std::string versionUtf8 = WideToUtf8(version);
if (opts.showVersion) {
std::wcout << L"winrar-keygen v" << version << L"\n";
@@ -257,6 +588,9 @@ int wmain(int argc, wchar_t* argv[]) {
ShowHelp(version);
return 0;
}
if (opts.checkUpdate) {
return CheckForUpdate(versionUtf8);
}
if (opts.activate && opts.outputFile != L"rarreg.key") {
std::wcerr << L"Error: --activate and -o cannot be used together.\n";
@@ -271,7 +605,7 @@ int wmain(int argc, wchar_t* argv[]) {
wchar_t appdata[MAX_PATH] = {};
DWORD len = ExpandEnvironmentStringsW(L"%APPDATA%\\WinRAR", appdata, MAX_PATH);
if (len == 0 || len > MAX_PATH) {
std::wcerr << L"Error: Failed to resolve %%APPDATA%% path.\n";
std::wcerr << L"Error: Failed to resolve %APPDATA% path.\n";
return -1;
}
CreateDirectoryW(appdata, nullptr);
@@ -355,3 +689,132 @@ int wmain(int argc, wchar_t* argv[]) {
return 0;
}
#else
int main(int argc, char* argv[]) {
Options opts;
if (!ParseArguments(argc, argv, opts)) {
return -1;
}
std::string version = GetAppVersion();
if (opts.showVersion) {
std::cout << "winrar-keygen v" << version << "\n";
return 0;
}
if (opts.showHelp) {
ShowHelp(version);
return 0;
}
if (opts.checkUpdate) {
return CheckForUpdate(version);
}
if (opts.activate && opts.outputFile != "rarreg.key") {
std::cerr << "Error: --activate and -o cannot be used together.\n";
return -1;
}
if (opts.activate && opts.textOnly) {
std::cerr << "Error: --activate and -t cannot be used together.\n";
return -1;
}
if (opts.activate) {
#ifdef __APPLE__
const char* home = getenv("HOME");
if (!home) {
std::cerr << "Error: Failed to resolve $HOME path.\n";
return -1;
}
std::string activateDir = std::string(home) + "/Library/Application Support/com.rarlab.WinRAR";
mkdir(activateDir.c_str(), 0755);
opts.outputFile = activateDir + "/rarreg.key";
#else
const char* home = getenv("HOME");
if (!home) {
std::cerr << "Error: Failed to resolve $HOME path.\n";
return -1;
}
opts.outputFile = std::string(home) + "/.rarkey";
#endif
}
try {
if (opts.username.empty() || opts.license.empty()) {
std::cerr << "Error: Username and License Name must not be empty.\n";
return -1;
}
if (opts.username.length() > 200 || opts.license.length() > 200) {
std::cerr << "Error: Username and License Name must not exceed 200 characters.\n";
return -1;
}
std::string displayUser = opts.username;
std::string displayLicense = opts.license;
std::string user, license;
switch (opts.encoding) {
case Encoding::UTF8: {
auto hasNonAscii = [](const std::string& s) {
for (unsigned char c : s)
if (c > 127) return true;
return false;
};
if (hasNonAscii(displayUser) &&
(displayUser.length() < 5 || displayUser.substr(0, 5) != "utf8:"))
displayUser = "utf8:" + displayUser;
if (hasNonAscii(displayLicense) &&
(displayLicense.length() < 5 || displayLicense.substr(0, 5) != "utf8:"))
displayLicense = "utf8:" + displayLicense;
user = displayUser;
license = displayLicense;
break;
}
case Encoding::ANSI:
std::cerr << "Warning: ANSI encoding is not supported on this platform. Using UTF-8.\n";
user = displayUser;
license = displayLicense;
break;
case Encoding::ASCII:
default:
user = displayUser;
license = displayLicense;
for (unsigned char c : user)
if (c > 127)
throw std::runtime_error(
"Username contains non-ASCII characters. Use '-e utf8'.");
for (unsigned char c : license)
if (c > 127)
throw std::runtime_error(
"License name contains non-ASCII characters. Use '-e utf8'.");
break;
}
auto Info = WinRarKeygen<WinRarConfig>::GenerateRegisterInfo(user.c_str(), license.c_str());
if (opts.textOnly) {
PrintRegisterInfo(Info, displayUser, displayLicense);
} else {
std::string content = BuildRegFileContent(Info);
if (!WriteRegFile(opts.outputFile, content)) {
std::cerr << "Error: Failed to write file: " << opts.outputFile << "\n";
return -1;
}
const char* encName = (opts.encoding == Encoding::UTF8) ? "UTF-8" :
(opts.encoding == Encoding::ANSI) ? "ANSI" : "ASCII";
std::cout << "\n";
PrintRegisterInfo(Info, displayUser, displayLicense);
std::cout << "\nDone! " << opts.outputFile << " has been generated. ("
<< encName << ")\n";
}
}
catch (std::exception& e) {
std::cerr << "Error: " << e.what() << "\n";
return -1;
}
return 0;
}
#endif
Binary file not shown.
Binary file not shown.
+7
View File
@@ -0,0 +1,7 @@
{
"name": "winrar-keygen",
"version": "4.1.0.0",
"dependencies": [
"gmp"
]
}
+4 -4
View File
@@ -51,8 +51,8 @@ END
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 4,0,1,0
PRODUCTVERSION 4,0,1,0
FILEVERSION 4,1,0,0
PRODUCTVERSION 4,1,0,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@@ -69,12 +69,12 @@ BEGIN
BEGIN
VALUE "CompanyName", "WinRAR Keygen"
VALUE "FileDescription", "WinRAR Key Generation Tool"
VALUE "FileVersion", "4.0.1.0"
VALUE "FileVersion", "4.1.0.0"
VALUE "InternalName", "winrar-keygen.exe"
VALUE "LegalCopyright", "Copyright (C) 2021 Bitcookies"
VALUE "OriginalFilename", "winrar-keygen.exe"
VALUE "ProductName", "WinRAR Keygen"
VALUE "ProductVersion", "4.0.1.0"
VALUE "ProductVersion", "4.1.0.0"
END
END
BLOCK "VarFileInfo"
+8 -2
View File
@@ -1,14 +1,16 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.29009.5
# Visual Studio Version 17
VisualStudioVersion = 17.14.37111.16
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "winrar-keygen", "winrar-keygen.vcxproj", "{2443AA55-9534-4451-9BCC-48AC0982A0CC}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|ARM64 = Debug|ARM64
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|ARM64 = Release|ARM64
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
@@ -21,6 +23,10 @@ Global
{2443AA55-9534-4451-9BCC-48AC0982A0CC}.Release|x64.Build.0 = Release|x64
{2443AA55-9534-4451-9BCC-48AC0982A0CC}.Release|x86.ActiveCfg = Release|Win32
{2443AA55-9534-4451-9BCC-48AC0982A0CC}.Release|x86.Build.0 = Release|Win32
{2443AA55-9534-4451-9BCC-48AC0982A0CC}.Debug|ARM64.ActiveCfg = Debug|ARM64
{2443AA55-9534-4451-9BCC-48AC0982A0CC}.Debug|ARM64.Build.0 = Debug|ARM64
{2443AA55-9534-4451-9BCC-48AC0982A0CC}.Release|ARM64.ActiveCfg = Release|ARM64
{2443AA55-9534-4451-9BCC-48AC0982A0CC}.Release|ARM64.Build.0 = Release|ARM64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
+77
View File
@@ -17,6 +17,14 @@
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|ARM64">
<Configuration>Debug</Configuration>
<Platform>ARM64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|ARM64">
<Configuration>Release</Configuration>
<Platform>ARM64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
@@ -26,6 +34,7 @@
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
<VcpkgTriplet Condition="'$(Platform)'=='Win32'">x86-windows-static</VcpkgTriplet>
<VcpkgTriplet Condition="'$(Platform)'=='x64'">x64-windows-static</VcpkgTriplet>
<VcpkgTriplet Condition="'$(Platform)'=='ARM64'">arm64-windows-static</VcpkgTriplet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
@@ -58,6 +67,21 @@
<CharacterSet>Unicode</CharacterSet>
<SpectreMitigation>false</SpectreMitigation>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
<SpectreMitigation>false</SpectreMitigation>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
<SpectreMitigation>false</SpectreMitigation>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
@@ -75,6 +99,12 @@
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
@@ -96,6 +126,16 @@
<OutDir>$(SolutionDir)bin\$(Platform)-$(Configuration)\</OutDir>
<IntDir>$(SolutionDir)obj\$(Platform)-$(Configuration)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)bin\$(Platform)-$(Configuration)\</OutDir>
<IntDir>$(SolutionDir)obj\$(Platform)-$(Configuration)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
<LinkIncremental>false</LinkIncremental>
<OutDir>$(SolutionDir)bin\$(Platform)-$(Configuration)\</OutDir>
<IntDir>$(SolutionDir)obj\$(Platform)-$(Configuration)\</IntDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
@@ -157,6 +197,43 @@
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpp17</LanguageStandard>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<AdditionalOptions>/utf-8 %(AdditionalOptions)</AdditionalOptions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpp17</LanguageStandard>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<AdditionalOptions>/utf-8 %(AdditionalOptions)</AdditionalOptions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpp17</LanguageStandard>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<AdditionalOptions>/utf-8 %(AdditionalOptions)</AdditionalOptions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>