diff --git a/Utilities/BEType.h b/Utilities/BEType.h index 4b34c7015e..e96c6f8499 100644 --- a/Utilities/BEType.h +++ b/Utilities/BEType.h @@ -545,13 +545,13 @@ public: type m_data; // don't access directly #endif - static_assert(!std::is_class::value, "be_t<> error: invalid type (class or structure)"); - static_assert(!std::is_union::value || std::is_same::value, "be_t<> error: invalid type (union)"); + static_assert(!std::is_class::value || std::is_same::value, "be_t<> error: invalid type (class or structure)"); + static_assert(!std::is_union::value || std::is_same::value || std::is_same::value, "be_t<> error: invalid type (union)"); static_assert(!std::is_pointer::value, "be_t<> error: invalid type (pointer)"); static_assert(!std::is_reference::value, "be_t<> error: invalid type (reference)"); static_assert(!std::is_array::value, "be_t<> error: invalid type (array)"); static_assert(!std::is_enum::value, "be_t<> error: invalid type (enumeration), use integral type instead"); - static_assert(__alignof(type) == __alignof(stype), "be_t<> error: unexpected alignment"); + static_assert(alignof(type) == alignof(stype), "be_t<> error: unexpected alignment"); be_t() = default; @@ -692,7 +692,7 @@ template struct is_be_t : public std::integral_constant< // to_be_t helper struct template struct to_be { - using type = std::conditional_t::value || std::is_enum::value || std::is_same::value, be_t, T>; + using type = std::conditional_t::value || std::is_enum::value || std::is_same::value || std::is_same::value, be_t, T>; }; // be_t if possible, T otherwise @@ -724,13 +724,13 @@ public: type m_data; // don't access directly - static_assert(!std::is_class::value, "le_t<> error: invalid type (class or structure)"); - static_assert(!std::is_union::value || std::is_same::value, "le_t<> error: invalid type (union)"); + static_assert(!std::is_class::value || std::is_same::value, "le_t<> error: invalid type (class or structure)"); + static_assert(!std::is_union::value || std::is_same::value || std::is_same::value, "le_t<> error: invalid type (union)"); static_assert(!std::is_pointer::value, "le_t<> error: invalid type (pointer)"); static_assert(!std::is_reference::value, "le_t<> error: invalid type (reference)"); static_assert(!std::is_array::value, "le_t<> error: invalid type (array)"); static_assert(!std::is_enum::value, "le_t<> error: invalid type (enumeration), use integral type instead"); - static_assert(__alignof(type) == __alignof(stype), "le_t<> error: unexpected alignment"); + static_assert(alignof(type) == alignof(stype), "le_t<> error: unexpected alignment"); le_t() = default; @@ -807,7 +807,7 @@ template struct is_le_t : public std::integral_constant< template struct to_le { - using type = std::conditional_t::value || std::is_enum::value || std::is_same::value, le_t, T>; + using type = std::conditional_t::value || std::is_enum::value || std::is_same::value || std::is_same::value, le_t, T>; }; // le_t if possible, T otherwise diff --git a/Utilities/GNU.h b/Utilities/GNU.h index d6e21fdf20..04bcbce45b 100644 --- a/Utilities/GNU.h +++ b/Utilities/GNU.h @@ -86,31 +86,147 @@ struct alignas(16) uint128_t { uint64_t lo, hi; - uint128_t& operator ++() + uint128_t() = default; + + uint128_t(uint64_t l) + : lo(l) + , hi(0) + { + } + + [[deprecated("Not implemented")]] inline uint128_t operator +(const uint128_t& r) const + { + return{}; + } + + inline uint128_t operator +(uint64_t r) const + { + uint128_t value; + value.lo = lo + r; + value.hi = value.lo < r ? hi + 1 : hi; + return value; + } + + [[deprecated("Not implemented")]] inline uint128_t operator -(const uint128_t& r) const + { + return{}; + } + + inline uint128_t operator -(uint64_t r) const + { + uint128_t value; + value.lo = lo - r; + value.hi = lo < r ? hi - 1 : hi; + return value; + } + + inline uint128_t operator +() const + { + return *this; + } + + inline uint128_t operator -() const + { + uint128_t value; + value.lo = ~lo + 1; + value.hi = lo ? ~hi : ~hi + 1; + return value; + } + + inline uint128_t& operator ++() { if (!++lo) ++hi; return *this; } - uint128_t& operator --() - { - if (!lo--) hi--; - return *this; - } - - uint128_t operator ++(int) + inline uint128_t operator ++(int) { uint128_t value = *this; if (!++lo) ++hi; return value; } - uint128_t operator --(int) + inline uint128_t& operator --() + { + if (!lo--) hi--; + return *this; + } + + inline uint128_t operator --(int) { uint128_t value = *this; if (!lo--) hi--; return value; } + + inline uint128_t operator ~() const + { + uint128_t value; + value.lo = ~lo; + value.hi = ~hi; + return value; + } + + inline uint128_t operator &(const uint128_t& r) const + { + uint128_t value; + value.lo = lo & r.lo; + value.hi = hi & r.hi; + return value; + } + + inline uint128_t operator |(const uint128_t& r) const + { + uint128_t value; + value.lo = lo | r.lo; + value.hi = hi | r.hi; + return value; + } + + inline uint128_t operator ^(const uint128_t& r) const + { + uint128_t value; + value.lo = lo ^ r.lo; + value.hi = hi ^ r.hi; + return value; + } + + [[deprecated("Not implemented")]] inline uint128_t& operator +=(const uint128_t& r) + { + return *this; + } + + inline uint128_t& operator +=(uint64_t r) + { + hi = (lo += r) < r ? hi + 1 : hi; + return *this; + } + + [[deprecated("Not implemented")]] inline uint128_t& operator -=(const uint128_t& r) + { + return *this; + } + + inline uint128_t& operator &=(const uint128_t& r) + { + lo &= r.lo; + hi &= r.hi; + return *this; + } + + inline uint128_t& operator |=(const uint128_t& r) + { + lo |= r.lo; + hi |= r.hi; + return *this; + } + + inline uint128_t& operator ^=(const uint128_t& r) + { + lo ^= r.lo; + hi ^= r.hi; + return *this; + } }; using __uint128_t = uint128_t; diff --git a/rpcs3/Crypto/unpkg.cpp b/rpcs3/Crypto/unpkg.cpp index fe2ce08277..5a0dcd62aa 100644 --- a/rpcs3/Crypto/unpkg.cpp +++ b/rpcs3/Crypto/unpkg.cpp @@ -6,13 +6,6 @@ #include "sha1.h" #include "key_vault.h" #include "unpkg.h" -#include "restore_new.h" -#pragma warning(push) -#pragma message("TODO: remove wx dependency: ") -#pragma warning(disable : 4996) -#include -#pragma warning(pop) -#include "define_new_memleakdetect.h" static bool CheckHeader(const fs::file& pkg_f, PKGHeader& header) { @@ -66,7 +59,7 @@ static bool CheckHeader(const fs::file& pkg_f, PKGHeader& header) } // PKG Decryption -bool Unpack(const fs::file& pkg_f, std::string dir) +bool UnpackPKG(const fs::file& pkg_f, const std::string& dir, volatile f64& progress) { // Save current file offset (probably zero) const u64 start_offset = pkg_f.seek(0, fsm::cur); @@ -85,75 +78,78 @@ bool Unpack(const fs::file& pkg_f, std::string dir) return false; } - aes_context c; - - u8 iv[HASH_LEN]; - be_t& hi = (be_t&)iv[0]; - be_t& lo = (be_t&)iv[8]; - - // Allocate buffers with BUF_SIZE size or more if required - const u64 buffer_size = std::max(BUF_SIZE, sizeof(PKGEntry) * header.file_count); - - const std::unique_ptr buf(new u8[buffer_size]), ctr(new u8[buffer_size]); - - // Debug key - u8 key[0x40] = {}; - memcpy(key + 0x00, &header.qa_digest[0], 8); // &data[0x60] - memcpy(key + 0x08, &header.qa_digest[0], 8); // &data[0x60] - memcpy(key + 0x10, &header.qa_digest[8], 8); // &data[0x68] - memcpy(key + 0x18, &header.qa_digest[8], 8); // &data[0x68] + // Allocate buffer with BUF_SIZE size or more if required + const std::unique_ptr buf(new u128[std::max(BUF_SIZE, sizeof(PKGEntry) * header.file_count) / sizeof(u128)]); // Define decryption subfunction (`psp` arg selects the key for specific block) - auto decrypt = [&](u64 offset, u64 size, bool psp) + auto decrypt = [&](u64 offset, u64 size, bool psp) -> u64 { - // Initialize buffer - std::memset(buf.get(), 0, size); - - // Read the data pkg_f.seek(start_offset + header.data_offset + offset); - size = pkg_f.read(buf.get(), size); - const u64 bits = (size + HASH_LEN - 1) / HASH_LEN; + + // Read the data and set available size + const u64 read = pkg_f.read(buf.get(), size); + + // Get block count + const u64 blocks = (read + 15) / 16; if (header.pkg_type == PKG_RELEASE_TYPE_DEBUG) { - for (u64 j = 0; j < bits; j++) + // Debug key + be_t input[8] = { - u8 hash[0x14]; - sha1(key, 0x40, hash); - *(u64*)&buf[j * HASH_LEN + 0] ^= *(u64*)&hash[0]; - *(u64*)&buf[j * HASH_LEN + 8] ^= *(u64*)&hash[8]; - *(be_t*)&key[0x38] += 1; + header.qa_digest[0], + header.qa_digest[0], + header.qa_digest[1], + header.qa_digest[1], + }; + + for (u64 i = 0; i < blocks; i++) + { + // Initialize "debug key" for current position + input[7] = offset / 16 + i; + + union + { + u8 _key[0x14]; + u128 key; + }; + + sha1(reinterpret_cast(input), sizeof(input), _key); + + buf[i] ^= key; } } if (header.pkg_type == PKG_RELEASE_TYPE_RELEASE) { + aes_context ctx; + // Set decryption key - aes_setkey_enc(&c, psp ? PKG_AES_KEY2 : PKG_AES_KEY, 128); + aes_setkey_enc(&ctx, psp ? PKG_AES_KEY2 : PKG_AES_KEY, 128); - // Initialize `iv` for the specific position - memcpy(iv, header.klicensee, sizeof(iv)); - if (lo + offset / HASH_LEN < lo) hi++; - lo += offset / HASH_LEN; + // Initialize "release key" for start position + be_t input = header.klicensee.value() + offset / 16; - for (u64 j = 0; j < bits; j++) + // Increment "release key" for every block + for (u64 i = 0; i < blocks; i++, input++) { - aes_crypt_ecb(&c, AES_ENCRYPT, iv, ctr.get() + j * HASH_LEN); - - if (!++lo) + union { - hi++; - } - } + u8 _key[16]; + u128 key; + }; - for (u64 j = 0; j < size; j++) - { - buf[j] ^= ctr[j]; + aes_crypt_ecb(&ctx, AES_ENCRYPT, reinterpret_cast(&input), _key); + + buf[i] ^= key; } } + + // Return the amount of data written in buf + return read; }; - wxProgressDialog pdlg("PKG Decrypter / Installer", "Please wait, decrypting...", header.file_count, 0, wxPD_AUTO_HIDE | wxPD_APP_MODAL); + LOG_SUCCESS(LOADER, "PKG: Installing in %s (%d entries)...", dir, header.file_count); decrypt(0, header.file_count * sizeof(PKGEntry), header.pkg_platform == PKG_PLATFORM_TYPE_PSP); @@ -161,12 +157,16 @@ bool Unpack(const fs::file& pkg_f, std::string dir) std::memcpy(entries.data(), buf.get(), entries.size() * sizeof(PKGEntry)); - for (s32 i = 0; i < entries.size(); i++) + for (const auto& entry : entries) { - const PKGEntry& entry = entries[i]; - const bool is_psp = (entry.type & PKG_FILE_ENTRY_PSP) != 0; + if (entry.name_size > 256) + { + LOG_ERROR(LOADER, "PKG: Name size is too big (0x%x)", entry.name_size); + continue; + } + decrypt(entry.name_offset, entry.name_size, is_psp); const std::string name(reinterpret_cast(buf.get()), entry.name_size); @@ -181,10 +181,7 @@ bool Unpack(const fs::file& pkg_f, std::string dir) { const std::string path = dir + name; - if (fs::is_file(path)) - { - LOG_WARNING(LOADER, "PKG Loader: '%s' is overwritten", path); - } + const bool did_overwrite = fs::is_file(path); if (fs::file out{ path, fom::write | fom::create | fom::trunc }) { @@ -192,15 +189,33 @@ bool Unpack(const fs::file& pkg_f, std::string dir) { const u64 block_size = std::min(BUF_SIZE, entry.file_size - pos); - decrypt(entry.file_offset + pos, block_size, is_psp); + if (decrypt(entry.file_offset + pos, block_size, is_psp) != block_size) + { + LOG_ERROR(LOADER, "PKG: Failed to extract file %s", path); + break; + } - out.write(buf.get(), block_size); + if (out.write(buf.get(), block_size) != block_size) + { + LOG_ERROR(LOADER, "PKG: Failed to write file %s", path); + break; + } + + progress += (block_size + 0.0) / header.data_size; + } + + if (did_overwrite) + { + LOG_SUCCESS(LOADER, "PKG: %s file overwritten", name); + } + else + { + LOG_SUCCESS(LOADER, "PKG: %s file created", name); } } else { - LOG_ERROR(LOADER, "PKG Loader: Could not create file '%s'", path); - return false; + LOG_ERROR(LOADER, "PKG: Could not create file %s", path); } break; @@ -210,10 +225,17 @@ bool Unpack(const fs::file& pkg_f, std::string dir) { const std::string path = dir + name; - if (!fs::is_dir(path) && !fs::create_dir(path)) + if (fs::create_dir(path)) { - LOG_ERROR(LOADER, "PKG Loader: Could not create directory: %s", path); - return false; + LOG_SUCCESS(LOADER, "PKG: %s directory created", name); + } + else if (fs::is_dir(path)) + { + LOG_SUCCESS(LOADER, "PKG: %s directory already exists", name); + } + else + { + LOG_ERROR(LOADER, "PKG: Could not create directory %s", path); } break; @@ -221,12 +243,9 @@ bool Unpack(const fs::file& pkg_f, std::string dir) default: { - LOG_ERROR(LOADER, "PKG Loader: unknown PKG file entry: 0x%x", entry.type); - return false; + LOG_ERROR(LOADER, "PKG: Unknown PKG entry type (0x%x) %s", entry.type, name); } } - - pdlg.Update(i + 1); } return true; diff --git a/rpcs3/Crypto/unpkg.h b/rpcs3/Crypto/unpkg.h index e973e892ef..4983525ebf 100644 --- a/rpcs3/Crypto/unpkg.h +++ b/rpcs3/Crypto/unpkg.h @@ -3,8 +3,7 @@ // Constants enum { - HASH_LEN = 16, - BUF_SIZE = 8192 * 1024, + BUF_SIZE = 8192 * 1024, // 8 MB PKG_HEADER_SIZE = 0xC0, //sizeof(pkg_header) + sizeof(pkg_unk_checksum) PKG_HEADER_SIZE2 = 0x280, }; @@ -45,8 +44,8 @@ struct PKGHeader be_t data_offset; // Encrypted data offset be_t data_size; // Encrypted data size in bytes char title_id[48]; // Title ID - u8 qa_digest[16]; // This should be the hash of "files + attribs" - u8 klicensee[16]; // Nonce + be_t qa_digest[2]; // This should be the hash of "files + attribs" + be_t klicensee; // Nonce }; struct PKGEntry @@ -59,6 +58,4 @@ struct PKGEntry be_t pad; // Padding (zeros) }; -namespace fs { struct file; } - -bool Unpack(const fs::file& pkg_f, std::string dir); +bool UnpackPKG(const struct fs::file& pkg_f, const std::string& dir, volatile f64& progress); diff --git a/rpcs3/Emu/System.cpp b/rpcs3/Emu/System.cpp index 544a227eba..2fed655a07 100644 --- a/rpcs3/Emu/System.cpp +++ b/rpcs3/Emu/System.cpp @@ -298,14 +298,14 @@ void Emulator::Run() SendDbgCommand(DID_STARTED_EMU); } -void Emulator::Pause() +bool Emulator::Pause() { const u64 start = get_system_time(); // try to set Paused status if (!sync_bool_compare_and_swap(&m_status, Running, Paused)) { - return; + return false; } // update pause start time @@ -322,6 +322,8 @@ void Emulator::Pause() } SendDbgCommand(DID_PAUSED_EMU); + + return true; } void Emulator::Resume() diff --git a/rpcs3/Emu/System.h b/rpcs3/Emu/System.h index 331efc0ea2..b0a4c43798 100644 --- a/rpcs3/Emu/System.h +++ b/rpcs3/Emu/System.h @@ -186,7 +186,7 @@ public: void Load(); void Run(); - void Pause(); + bool Pause(); void Resume(); void Stop(); diff --git a/rpcs3/Gui/MainFrame.cpp b/rpcs3/Gui/MainFrame.cpp index 5e88894358..1c1928a495 100644 --- a/rpcs3/Gui/MainFrame.cpp +++ b/rpcs3/Gui/MainFrame.cpp @@ -22,8 +22,9 @@ #include "Gui/MemoryStringSearcher.h" #include "Gui/LLEModulesManager.h" #include "Gui/CgDisasm.h" -#include "Loader/PKG.h" +#include "Crypto/unpkg.h" #include +#include BEGIN_EVENT_TABLE(MainFrame, FrameBase) EVT_CLOSE(MainFrame::OnQuit) @@ -229,33 +230,81 @@ void MainFrame::BootGame(wxCommandEvent& WXUNUSED(event)) void MainFrame::InstallPkg(wxCommandEvent& WXUNUSED(event)) { - bool stopped = false; - - if(Emu.IsRunning()) - { - Emu.Pause(); - stopped = true; - } + const bool was_running = Emu.Pause(); wxFileDialog ctrl(this, L"Select PKG", wxEmptyString, wxEmptyString, "PKG files (*.pkg)|*.pkg|All files (*.*)|*.*", wxFD_OPEN | wxFD_FILE_MUST_EXIST); - if(ctrl.ShowModal() == wxID_CANCEL) + if (ctrl.ShowModal() == wxID_CANCEL) { - if(stopped) Emu.Resume(); + if (was_running) Emu.Resume(); return; } Emu.Stop(); - - // Open and install PKG file - fs::file pkg_f(ctrl.GetPath().ToStdString(), fom::read); - if (pkg_f) + Emu.GetVFS().Init("/"); + std::string local_path; + Emu.GetVFS().GetDevice("/dev_hdd0/game/", local_path); + + // Open PKG file + fs::file pkg_f{ ctrl.GetPath().ToStdString() }; + + if (!pkg_f) { - Emu.GetVFS().Init("/"); - std::string local_path; - Emu.GetVFS().GetDevice("/dev_hdd0/game/", local_path); - PKGLoader::Install(pkg_f, local_path + "/"); + LOG_ERROR(LOADER, "PKG: Failed to open %s", ctrl.GetPath().ToStdString()); + return; + } + + // Fetch title ID from the header + char title_id[10] = "?????????"; + pkg_f.seek(55); + pkg_f.read(title_id, 9); + pkg_f.seek(0); + + // Append title ID to the path + local_path += '/'; + local_path += title_id; + + if (!fs::create_dir(local_path)) + { + if (fs::is_dir(local_path)) + { + if (wxMessageDialog(this, "Another installation found. Do you want to overwrite it?", "PKG Decrypter / Installer", wxYES_NO | wxCENTRE).ShowModal() != wxID_YES) + { + LOG_ERROR(LOADER, "PKG: Cancelled installation to existing directory %s", local_path); + return; + } + } + else + { + LOG_ERROR(LOADER, "PKG: Could not create the installation directory %s", local_path); + return; + } + } + + wxProgressDialog pdlg("PKG Decrypter / Installer", "Please wait, unpacking...", 1000, this, wxPD_AUTO_HIDE | wxPD_APP_MODAL); + + volatile f64 progress = 0.0; + + // Run PKG unpacking asynchronously + auto result = std::async(WRAP_EXPR(UnpackPKG(pkg_f, local_path + "/", progress))); + + // Wait for the completion + while (result.wait_for(15ms) != std::future_status::ready) + { + // Update progress window + pdlg.Update(progress * pdlg.GetRange()); + + // Update main frame + Update(); + wxGetApp().ProcessPendingEvents(); + } + + pdlg.Close(); + + if (result.get()) + { + LOG_SUCCESS(LOADER, "PKG: Package successfully installed in %s", local_path); // Refresh game list m_game_viewer->Refresh(); diff --git a/rpcs3/Loader/PKG.cpp b/rpcs3/Loader/PKG.cpp deleted file mode 100644 index d452917234..0000000000 --- a/rpcs3/Loader/PKG.cpp +++ /dev/null @@ -1,49 +0,0 @@ -#include "stdafx.h" -#include "Utilities/Log.h" -#include "Utilities/rMsgBox.h" -#include "Utilities/File.h" -#include "PKG.h" -#include "../Crypto/unpkg.h" - -bool PKGLoader::Install(const fs::file& pkg_f, std::string dest) -{ - // Initial checks - if (!pkg_f) - { - return false; - } - - // Fetch title ID from the header. - char title_id[48]; - pkg_f.seek(48); - pkg_f.read(title_id, 48); - pkg_f.seek(0); - - std::string titleID = std::string(title_id).substr(7, 9); - - if (fs::is_dir(dest + titleID)) - { - if (rMessageDialog(NULL, "Another installation found. Do you want to overwrite it?", "PKG Decrypter / Installer", rYES_NO | rCENTRE).ShowModal() != rID_YES) - { - LOG_ERROR(LOADER, "PKG Loader: Another installation found in: %s", titleID.c_str()); - return false; - } - } - else if (!fs::create_dir(dest + titleID)) - { - LOG_ERROR(LOADER, "PKG Loader: Could not create the installation directory: %s", titleID.c_str()); - return false; - } - - // Decrypt and unpack the PKG file. - if (!Unpack(pkg_f, dest + titleID + "/")) - { - LOG_ERROR(LOADER, "PKG Loader: Failed to install package!"); - return false; - } - else - { - LOG_NOTICE(LOADER, "PKG Loader: Package successfully installed in: /dev_hdd0/game/%s", titleID.c_str()); - return true; - } -} diff --git a/rpcs3/Loader/PKG.h b/rpcs3/Loader/PKG.h deleted file mode 100644 index 1a51c71199..0000000000 --- a/rpcs3/Loader/PKG.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -namespace fs { struct file; } - -struct PKGLoader -{ - static bool Install(const fs::file& pkg_f, std::string dest); -}; diff --git a/rpcs3/emucore.vcxproj b/rpcs3/emucore.vcxproj index 3da301472e..84ba49f030 100644 --- a/rpcs3/emucore.vcxproj +++ b/rpcs3/emucore.vcxproj @@ -349,7 +349,6 @@ - @@ -662,7 +661,6 @@ - diff --git a/rpcs3/emucore.vcxproj.filters b/rpcs3/emucore.vcxproj.filters index 95534f0105..bb13a7ca4f 100644 --- a/rpcs3/emucore.vcxproj.filters +++ b/rpcs3/emucore.vcxproj.filters @@ -386,9 +386,6 @@ Loader - - Loader - Loader @@ -1327,9 +1324,6 @@ Loader - - Loader - Loader diff --git a/rpcs3/stdafx.h b/rpcs3/stdafx.h index ee829d5339..5bf3ddf6f9 100644 --- a/rpcs3/stdafx.h +++ b/rpcs3/stdafx.h @@ -40,13 +40,15 @@ #include #include #include +#include using namespace std::string_literals; +using namespace std::chrono_literals; #include "Utilities/GNU.h" #define CHECK_SIZE(type, size) static_assert(sizeof(type) == size, "Invalid " #type " type size") -#define CHECK_ALIGN(type, align) static_assert(__alignof(type) == align, "Invalid " #type " type alignment") +#define CHECK_ALIGN(type, align) static_assert(alignof(type) == align, "Invalid " #type " type alignment") #define CHECK_MAX_SIZE(type, size) static_assert(sizeof(type) <= size, #type " type size is too big") #define CHECK_SIZE_ALIGN(type, size, align) CHECK_SIZE(type, size); CHECK_ALIGN(type, align) #define CHECK_ASCENDING(constexpr_array) static_assert(::is_ascending(constexpr_array), #constexpr_array " is not sorted in ascending order") @@ -154,7 +156,7 @@ template struct triplet_t #define sizeof32(type) static_cast(sizeof(type)) // return 32 bit alignof() to avoid widening/narrowing conversions with size_t -#define alignof32(type) static_cast(__alignof(type)) +#define alignof32(type) static_cast(alignof(type)) #define WRAP_EXPR(expr) [&]{ return expr; } #define COPY_EXPR(expr) [=]{ return expr; }