Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 42 additions & 14 deletions WechatExporter.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ARCHS = arm64;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
Expand Down Expand Up @@ -418,18 +419,20 @@
GCC_WARN_UNUSED_VARIABLE = YES;
HEADER_SEARCH_PATHS = /usr/local/include/;
LIBRARY_SEARCH_PATHS = /usr/local/lib;
MACOSX_DEPLOYMENT_TARGET = 10.10;
MACOSX_DEPLOYMENT_TARGET = 10.15;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = macosx;
VALID_ARCHS = arm64;
};
name = Debug;
};
343F6125252322D600FFE085 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ARCHS = arm64;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
Expand Down Expand Up @@ -474,17 +477,20 @@
GCC_WARN_UNUSED_VARIABLE = YES;
HEADER_SEARCH_PATHS = /usr/local/include/;
LIBRARY_SEARCH_PATHS = /usr/local/lib;
MACOSX_DEPLOYMENT_TARGET = 10.10;
MACOSX_DEPLOYMENT_TARGET = 10.15;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
SDKROOT = macosx;
VALID_ARCHS = arm64;
};
name = Release;
};
343F6127252322D600FFE085 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ARCHS = arm64;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CODE_SIGN_ENTITLEMENTS = WechatExporter/WechatExporter.entitlements;
CODE_SIGN_IDENTITY = "-";
CODE_SIGN_STYLE = Automatic;
Expand All @@ -500,31 +506,43 @@
HEADER_SEARCH_PATHS = (
/usr/local/include/,
"${SDKROOT}/usr/include/libxml2/",
/opt/homebrew/Cellar/lame/3.100/include,
/opt/homebrew/include,
/Users/caoye/git/WechatExporter/jsoncpp/include,
"/opt/homebrew/opt/protobuf@21/include",
);
INFOPLIST_FILE = WechatExporter/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 10.10;
LIBRARY_SEARCH_PATHS = (
/usr/local/lib,
"/opt/homebrew/opt/protobuf@21/lib",
);
MACOSX_DEPLOYMENT_TARGET = 10.15;
MARKETING_VERSION = 1.9.0;
OTHER_CPLUSPLUSFLAGS = "$(OTHER_CFLAGS)";
OTHER_LDFLAGS = (
"-L/usr/local/lib",
"-lprotobufd",
"-ljsoncpp",
"-lmp3lame",
"-lSKP_SILK_SDK",
"-lplist-2.0",
/opt/homebrew/lib/libmp3lame.a,
/opt/homebrew/lib/libSKP_SILK_SDK.a,
"/opt/homebrew/lib/libplist-2.0.a",
"/opt/homebrew/opt/protobuf@21/lib/libprotobuf.a",
/Users/caoye/git/WechatExporter/jsoncpp/build/lib/libjsoncpp.a,
);
PRODUCT_BUNDLE_IDENTIFIER = org.wakin.WechatExporter;
PRODUCT_NAME = "$(TARGET_NAME)";
VALID_ARCHS = arm64;
};
name = Debug;
};
343F6128252322D600FFE085 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ARCHS = arm64;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CODE_SIGN_ENTITLEMENTS = WechatExporter/WechatExporter.entitlements;
CODE_SIGN_IDENTITY = "-";
CODE_SIGN_STYLE = Automatic;
Expand All @@ -539,24 +557,34 @@
HEADER_SEARCH_PATHS = (
/usr/local/include/,
"${SDKROOT}/usr/include/libxml2/",
/opt/homebrew/Cellar/lame/3.100/include,
/opt/homebrew/include,
/Users/caoye/git/WechatExporter/jsoncpp/include,
"/opt/homebrew/opt/protobuf@21/include",
);
INFOPLIST_FILE = WechatExporter/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 10.10;
LIBRARY_SEARCH_PATHS = (
/usr/local/lib,
"/opt/homebrew/opt/protobuf@21/lib",
);
MACOSX_DEPLOYMENT_TARGET = 10.15;
MARKETING_VERSION = 1.9.0;
OTHER_CPLUSPLUSFLAGS = "$(OTHER_CFLAGS)";
OTHER_LDFLAGS = (
"-L/usr/local/lib",
"-lprotobuf",
"-ljsoncpp",
"-lmp3lame",
"-lSKP_SILK_SDK",
"-lplist-2.0",
/opt/homebrew/lib/libmp3lame.a,
/opt/homebrew/lib/libSKP_SILK_SDK.a,
"/opt/homebrew/lib/libplist-2.0.a",
"/opt/homebrew/opt/protobuf@21/lib/libprotobuf.a",
/Users/caoye/git/WechatExporter/jsoncpp/build/lib/libjsoncpp.a,
);
PRODUCT_BUNDLE_IDENTIFIER = org.wakin.WechatExporter;
PRODUCT_NAME = "$(TARGET_NAME)";
VALID_ARCHS = arm64;
};
name = Release;
};
Expand Down
2 changes: 2 additions & 0 deletions WechatExporter/HttpHelper.mm
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
#define PROCESSOR "PPC"
#elif defined(__i386__) || defined(__x86_64__)
#define PROCESSOR "Intel"
#elif defined(__arm64__)
#define PROCESSOR "ARM64"
#else
#error Unknown architecture
#endif
Expand Down
88 changes: 68 additions & 20 deletions WechatExporter/core/Exporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -782,11 +782,19 @@ int Exporter::exportSession(const Friend& user, const MessageParser& msgParser,
if (session.getRecordCount() > 0)
{
messages.reserve(session.getRecordCount());
m_logger->write(formatString("Session %s: Reserved memory for %d messages",
session.getUsrName().c_str(), session.getRecordCount()));
}

int64_t maxMsgId = 0;
m_exportContext->getMaxId(session.getUsrName(), maxMsgId);

if (maxMsgId > 0)
{
m_logger->write(formatString("Session %s: Using incremental export, minId=%lld (will skip messages with ID <= %lld)",
session.getUsrName().c_str(), maxMsgId, maxMsgId));
}

int numberOfMsgs = 0;
SessionParser sessionParser(m_options);
std::unique_ptr<SessionParser::MessageEnumerator> enumerator(sessionParser.buildMsgEnumerator(session, maxMsgId));
Expand All @@ -801,9 +809,21 @@ int Exporter::exportSession(const Friend& user, const MessageParser& msgParser,

tvs.clear();
msgParser.parse(msg, session, tvs);
exportMessage(session, tvs, messages);
if (exportMessage(session, tvs, messages))
{
m_logger->write(formatString("Session %s: Stopping message processing due to error",
session.getUsrName().c_str()));
break;
}
++numberOfMsgs;

// Log progress every 10000 messages to track where it stops
if (numberOfMsgs % 10000 == 0)
{
m_logger->write(formatString("Session %s: Processed %d messages, vector size: %zu",
session.getUsrName().c_str(), numberOfMsgs, messages.size()));
}

notifySessionProgress(session.getUsrName(), session.getData(), numberOfMsgs, session.getRecordCount());
if (m_cancelled)
{
Expand Down Expand Up @@ -837,6 +857,11 @@ int Exporter::exportSession(const Friend& user, const MessageParser& msgParser,
const size_t numberOfMessages = std::distance(e, messages.cend());
const size_t numberOfPages = (numberOfMessages + pageSize - 1) / pageSize;

// Debug: Log pagination settings
m_logger->write(formatString("Session %s: Pagination settings - pageSize=%zu, numberOfMessages=%zu, numberOfPages=%zu, SPO_SYNC_LOADING=%s",
session.getUsrName().c_str(), pageSize, numberOfMessages, numberOfPages,
(m_options & SPO_SYNC_LOADING) ? "enabled" : "disabled"));

std::string html = getTemplate("frame");
#ifndef NDEBUG
replaceAll(html, "%%USRNAME%%", user.getUsrName() + " - " + user.getHash());
Expand Down Expand Up @@ -865,24 +890,29 @@ int Exporter::exportSession(const Friend& user, const MessageParser& msgParser,
{
std::string dataPath = combinePath(outputBase, session.getOutputFileName() + "_files", "Data");
makeDirectory(dataPath);
m_logger->write(formatString("Session %s: Creating %zu pagination files in %s",
session.getUsrName().c_str(), numberOfPages, dataPath.c_str()));

for (size_t page = 0; page < numberOfPages; ++page)
{
b = e;
std::string scripts = getTemplate("scripts");
e = (page == (numberOfPages - 1)) ? messages.cend() : (b + pageSize);

// Generate JSON data for this page
Json::Value jsonMsgs(Json::arrayValue);
for (auto it = b; it != e; ++it)
{
jsonMsgs.append(*it);
}
Json::StreamWriterBuilder builder;
builder["indentation"] = ""; // assume default for comments is None
#ifndef NDEBUG
#ifndef NDEBUG
builder["emitUTF8"] = true;
#endif
#endif
std::string moreMsgs = Json::writeString(builder, jsonMsgs);

// Replace the JSON data placeholder with actual data
replaceAll(scripts, "%%JSON_DATA%%", moreMsgs);

fileName = combinePath(dataPath, "msg-" + std::to_string(page + 1) + ".js");
Expand All @@ -904,7 +934,23 @@ bool Exporter::exportMessage(const Session& session, const std::vector<TemplateV
content.append(buildContentFromTemplateValues(*it));
}

messages.push_back(content);
try
{
messages.push_back(content);
}
catch (const std::bad_alloc& e)
{
m_logger->write(formatString("Session %s: Memory allocation failed at message %zu: %s",
session.getUsrName().c_str(), messages.size(), e.what()));
return true; // Signal to stop processing
}
catch (const std::exception& e)
{
m_logger->write(formatString("Session %s: Error adding message %zu: %s",
session.getUsrName().c_str(), messages.size(), e.what()));
return true; // Signal to stop processing
}

return m_cancelled;
}

Expand Down Expand Up @@ -1104,22 +1150,24 @@ bool Exporter::loadStrings()
return false;
}

Json::Reader reader;
Json::Value value;
if (reader.parse(readFile(path), value))
{
int sz = value.size();
for (int idx = 0; idx < sz; ++idx)
{
std::string k = value[idx]["key"].asString();
std::string v = value[idx]["value"].asString();
if (m_localeStrings.find(k) != m_localeStrings.cend())
{
// return false;
}
m_localeStrings[k] = v;
}
}
// Json::Reader reader;
// Json::Value value;
// if (reader.parse(readFile(path), value))
// {
// int sz = value.size();
// for (int idx = 0; idx < sz; ++idx)
// {
// std::string k = value[idx]["key"].asString();
// std::string v = value[idx]["value"].asString();
// if (m_localeStrings.find(k) != m_localeStrings.cend())
// {
// // return false;
// }
// m_localeStrings[k] = v;
// }
// }

// Skip JSON parsing for locale strings - use default behavior

return true;
}
Expand Down
9 changes: 7 additions & 2 deletions WechatExporter/core/ITunesParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,11 @@ unsigned int ITunesDb::parseModifiedTime(const std::vector<unsigned char>& data)
}
uint64_t val = 0;
plist_t node = NULL;
plist_from_memory(reinterpret_cast<const char *>(&data[0]), static_cast<uint32_t>(data.size()), &node);
plist_format_t fmt;
plist_from_memory(reinterpret_cast<const char*>(&data[0]),
data.size(),
&node,
&fmt);
if (NULL != node)
{
plist_t lastModified = plist_access_path(node, 3, "$objects", 1, "LastModified");
Expand Down Expand Up @@ -697,7 +701,8 @@ bool ManifestParser::parse(const std::string& path, BackupManifest& manifest) co
if (readFile(fileName, data))
{
plist_t node = NULL;
plist_from_memory(reinterpret_cast<const char *>(&data[0]), static_cast<uint32_t>(data.size()), &node);
plist_format_t fmt;
plist_from_memory(reinterpret_cast<const char *>(&data[0]), data.size(), &node, &fmt);
if (NULL != node)
{
plist_t isEncryptedNode = plist_access_path(node, 1, "IsEncrypted");
Expand Down
15 changes: 9 additions & 6 deletions WechatExporter/core/MessageParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -601,12 +601,15 @@ void MessageParser::parseNotice(const WXMSG& msg, const Session& session, Templa
#endif
tv.setName("notice");

Json::Reader reader;
Json::Value root;
if (reader.parse(msg.content, root))
{
tv["%%MESSAGE%%"] = root["msgContent"].asString();
}
// Json::Reader reader;
// Json::Value root;
// if (reader.parse(msg.content, root))
// {
// tv["%%MESSAGE%%"] = root["msgContent"].asString();
// }

// Use raw content instead of JSON parsing
tv["%%MESSAGE%%"] = msg.content;
}

void MessageParser::parseSysNotice(const WXMSG& msg, const Session& session, TemplateValues& tv) const
Expand Down
Loading