├── .gitignore ├── LICENSE ├── README.md ├── WechatExporter.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── xcshareddata │ └── xcschemes │ └── WechatExporter.xcscheme ├── WechatExporter.xcworkspace ├── contents.xcworkspacedata └── xcshareddata │ └── IDEWorkspaceChecks.plist ├── WechatExporter ├── AppConfiguration.h ├── AppConfiguration.mm ├── AppDelegate.h ├── AppDelegate.mm ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ ├── 1024.png │ │ ├── 128.png │ │ ├── 16.png │ │ ├── 256.png │ │ ├── 32.png │ │ ├── 512.png │ │ ├── 64.png │ │ └── Contents.json │ ├── Contents.json │ ├── MainMenuCN.imageset │ │ ├── Contents.json │ │ ├── main-menu-cn@1x.png │ │ ├── main-menu-cn@2x.png │ │ └── main-menu-cn@3x.png │ └── MainMenuEN.imageset │ │ ├── Contents.json │ │ ├── main-menu-en@1x.png │ │ ├── main-menu-en@2x.png │ │ └── main-menu-en@3x.png ├── Base.lproj │ └── Main.storyboard ├── ExportNotifierImpl.h ├── HttpHelper.h ├── HttpHelper.mm ├── Info.plist ├── LICENSES │ ├── jsoncpp.LICENSE │ ├── lame.LICENSE │ ├── libcurl.COPYING │ ├── libimobiledevice-glue.COPYING │ ├── libimobiledevice.COPYING.LESSER │ ├── libplist.COPYING.LESSER │ ├── libusbmuxd.COPYING │ ├── libxml2.Copyright │ ├── opencore-amr.LICENSE │ ├── openssl.LICENSE │ └── protobuf.LICENSE ├── LoggerImpl.h ├── PdfConverterImpl.h ├── SessionDataSource.h ├── SessionDataSource.mm ├── ViewController copy.mm ├── ViewController.h ├── ViewController.mm ├── WechatExporter.entitlements ├── core │ ├── AsyncExecutor.cpp │ ├── AsyncExecutor.h │ ├── AsyncTask.cpp │ ├── AsyncTask.h │ ├── ByteArrayLocater.h │ ├── DownloadPool.cpp │ ├── DownloadPool.h │ ├── Downloader.cpp │ ├── Downloader.h │ ├── ExportContext.h │ ├── ExportNotifier.h │ ├── ExportOption.h │ ├── Exporter.cpp │ ├── Exporter.h │ ├── FileSystem.cpp │ ├── FileSystem.h │ ├── IDeviceBackup.cpp │ ├── IDeviceBackup.h │ ├── ITunesParser.cpp │ ├── ITunesParser.h │ ├── Logger.h │ ├── MMKVReader.h │ ├── MbdbReader.h │ ├── MessageParser.cpp │ ├── MessageParser.h │ ├── PdfConverter.h │ ├── RawMessage.cpp │ ├── RawMessage.h │ ├── ResManager.cpp │ ├── ResManager.h │ ├── TaskManager.cpp │ ├── TaskManager.h │ ├── Template.cpp │ ├── Template.h │ ├── Updater.cpp │ ├── Updater.h │ ├── Utils.cpp │ ├── Utils.h │ ├── Utils_audio.cpp │ ├── Utils_md5.cpp │ ├── Utils_protobuf.cpp │ ├── Utils_silk.cpp │ ├── Utils_thread.cpp │ ├── Utils_xml.cpp │ ├── WechatObjects.h │ ├── WechatParser.cpp │ ├── WechatParser.h │ ├── WechatSource.h │ ├── XmlParser.cpp │ ├── XmlParser.h │ ├── endianness.h │ ├── md5.c │ ├── md5.h │ └── semaphore.h ├── en.lproj │ ├── Localizable.strings │ └── Main.strings ├── main.m ├── res │ ├── DefaultAppIcon.png │ ├── DefaultAvatar.png │ ├── emoji │ │ ├── emoji.json │ │ └── images │ │ │ ├── Aaagh!.png │ │ │ ├── Angry.png │ │ │ ├── Awesome.png │ │ │ ├── Awkward.png │ │ │ ├── Bah!L.png │ │ │ ├── Bah!R.png │ │ │ ├── Basketball.png │ │ │ ├── Beckon.png │ │ │ ├── Beer.png │ │ │ ├── Blessing.png │ │ │ ├── Blowkiss.png │ │ │ ├── Blush.png │ │ │ ├── Bomb.png │ │ │ ├── Boring.png │ │ │ ├── Broken.png │ │ │ ├── BrokenHeart.png │ │ │ ├── Bye.png │ │ │ ├── Cake.png │ │ │ ├── Candle.png │ │ │ ├── Chick.png │ │ │ ├── Chuckle.png │ │ │ ├── Clap.png │ │ │ ├── Cleaver.png │ │ │ ├── Coffee.png │ │ │ ├── Commando.png │ │ │ ├── Concerned.png │ │ │ ├── CoolGuy.png │ │ │ ├── Cry.png │ │ │ ├── Dagger.png │ │ │ ├── Determined.png │ │ │ ├── Dizzy.png │ │ │ ├── Doge.png │ │ │ ├── Dramatic.png │ │ │ ├── Drool.png │ │ │ ├── Drowsy.png │ │ │ ├── Duh.png │ │ │ ├── Emm.png │ │ │ ├── Facepalm.png │ │ │ ├── Fight.png │ │ │ ├── Firecracker.png │ │ │ ├── Fireworks.png │ │ │ ├── Fist.png │ │ │ ├── Flushed.png │ │ │ ├── Frown.png │ │ │ ├── Gift.png │ │ │ ├── GoForIt.png │ │ │ ├── Grimace.png │ │ │ ├── Grin.png │ │ │ ├── Hammer.png │ │ │ ├── Happy.png │ │ │ ├── Heart.png │ │ │ ├── Hey.png │ │ │ ├── Hooray.png │ │ │ ├── Hug.png │ │ │ ├── Hungry.png │ │ │ ├── Hurt.png │ │ │ ├── InLove.png │ │ │ ├── Joyful.png │ │ │ ├── JumpRope.png │ │ │ ├── KeepFighting.png │ │ │ ├── Kiss.png │ │ │ ├── Kotow.png │ │ │ ├── Ladybug.png │ │ │ ├── Laugh.png │ │ │ ├── Let Down.png │ │ │ ├── LetMeSee.png │ │ │ ├── Lightning.png │ │ │ ├── Lips.png │ │ │ ├── Lol.png │ │ │ ├── Meditate.png │ │ │ ├── Moon.png │ │ │ ├── Moue.png │ │ │ ├── MyBad.png │ │ │ ├── NoProb.png │ │ │ ├── NosePick.png │ │ │ ├── Nuh-uh.png │ │ │ ├── OK.png │ │ │ ├── OMG.png │ │ │ ├── Onlooker.png │ │ │ ├── Packet.png │ │ │ ├── Panic.png │ │ │ ├── Party.png │ │ │ ├── Peace.png │ │ │ ├── Pig.png │ │ │ ├── PingPong.png │ │ │ ├── Pinky.png │ │ │ ├── Pooh-pooh.png │ │ │ ├── Poop.png │ │ │ ├── Puke.png │ │ │ ├── Pup.png │ │ │ ├── Respect.png │ │ │ ├── Rice.png │ │ │ ├── Rich.png │ │ │ ├── RockOn.png │ │ │ ├── Rose.png │ │ │ ├── Ruthless.png │ │ │ ├── Scold.png │ │ │ ├── Scowl.png │ │ │ ├── Scream.png │ │ │ ├── Shake.png │ │ │ ├── Shame.png │ │ │ ├── Shhh.png │ │ │ ├── Shocked.png │ │ │ ├── Shrunken.png │ │ │ ├── Shy.png │ │ │ ├── Sick.png │ │ │ ├── Sigh.png │ │ │ ├── Silent.png │ │ │ ├── Skull.png │ │ │ ├── Sleep.png │ │ │ ├── Slight.png │ │ │ ├── Sly.png │ │ │ ├── Smart.png │ │ │ ├── Smile.png │ │ │ ├── Smirk.png │ │ │ ├── Smooch.png │ │ │ ├── Smug.png │ │ │ ├── Sob.png │ │ │ ├── Soccer.png │ │ │ ├── Speechless.png │ │ │ ├── Sun.png │ │ │ ├── Surprise.png │ │ │ ├── Surrender.png │ │ │ ├── Sweat.png │ │ │ ├── Sweats.png │ │ │ ├── TaiChi L.png │ │ │ ├── TaiChi R.png │ │ │ ├── Tea.png │ │ │ ├── TearingUp.png │ │ │ ├── Terror.png │ │ │ ├── ThumbsDown.png │ │ │ ├── ThumbsUp.png │ │ │ ├── Toasted.png │ │ │ ├── Tongue.png │ │ │ ├── Tormented.png │ │ │ ├── Tremble.png │ │ │ ├── Trick.png │ │ │ ├── Twirl.png │ │ │ ├── Waddle.png │ │ │ ├── Watermelon.png │ │ │ ├── Wave.png │ │ │ ├── Whimper.png │ │ │ ├── Wilt.png │ │ │ ├── Worship.png │ │ │ ├── Wow.png │ │ │ ├── Wrath.png │ │ │ ├── Yawn.png │ │ │ ├── Yeah!.png │ │ │ ├── u1F47B.png │ │ │ ├── u1F4AA.png │ │ │ ├── u1F604.png │ │ │ ├── u1F61D.png │ │ │ └── u1F633.png │ ├── en.txt │ ├── iPhone.png │ ├── iTunes.png │ ├── templates │ │ ├── audio.html │ │ ├── card.html │ │ ├── channels.html │ │ ├── emoji.html │ │ ├── filter.html │ │ ├── frame.html │ │ ├── frame_filter.html │ │ ├── image.html │ │ ├── listframe.html │ │ ├── listitem.html │ │ ├── member.html │ │ ├── members.html │ │ ├── msg.html │ │ ├── notice.html │ │ ├── plainshare.html │ │ ├── refermsg.html │ │ ├── scripts.html │ │ ├── share.html │ │ ├── system.html │ │ ├── thumb.html │ │ ├── transfer.html │ │ ├── video.html │ │ ├── videonew.html │ │ └── wxemoji.html │ ├── templates_txt │ │ ├── audio.html │ │ ├── card.html │ │ ├── channels.html │ │ ├── emoji.html │ │ ├── frame.html │ │ ├── image.html │ │ ├── listframe.html │ │ ├── listitem.html │ │ ├── msg.html │ │ ├── notice.html │ │ ├── plainshare.html │ │ ├── refermsg.html │ │ ├── scripts.html │ │ ├── share.html │ │ ├── system.html │ │ ├── thumb.html │ │ ├── transfer.html │ │ ├── video.html │ │ └── wxemoji.html │ └── zh-Hans.txt └── zh-Hans.lproj │ ├── Localizable.strings │ └── Main.strings ├── WechatExporterCmd.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── xcshareddata │ └── xcschemes │ └── WechatExporterCmd.xcscheme ├── WechatExporterCmd ├── WechatExporter.cpp └── WechatExporterCmd.h ├── docs ├── index.html ├── screenshots │ ├── mac.png │ ├── screenshot-mac.png │ ├── screenshot-win.png │ └── win.png └── update.conf ├── libplist.README.md ├── release └── vcproject ├── AboutDlg.h ├── AppConfiguration.cpp ├── AppConfiguration.h ├── BackupDlg.h ├── ColoredControls.h ├── Core.h ├── ExportNotifierImpl.h ├── ITunesDetector.h ├── LogListBox.h ├── LoggerImpl.h ├── MainFrm.h ├── PdfConverterImpl.h ├── ProgressListViewCtrl.h ├── TextProgressBarCtrl.h ├── ToolTipButton.h ├── VersionDetector.h ├── View.h ├── ViewHelper.cpp ├── ViewHelper.h ├── WechatExporter.cpp ├── WechatExporter.h ├── WechatExporter.rc ├── WechatExporter.sln ├── WechatExporter.vcxproj ├── WechatExporter.vcxproj.filters ├── WechatExporterCmd.cpp ├── WechatExporterCmd.vcxproj ├── WechatExporterCmd.vcxproj.filters ├── res ├── WechatExporter.ico ├── iPhone.ico ├── iTunes.ico ├── idr_main.ico └── toolbar.bmp ├── resource.h ├── stdafx.cpp └── stdafx.h /WechatExporter.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /WechatExporter.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /WechatExporter.xcodeproj/xcshareddata/xcschemes/WechatExporter.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 45 | 51 | 52 | 53 | 54 | 60 | 62 | 68 | 69 | 70 | 71 | 73 | 74 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /WechatExporter.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /WechatExporter.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /WechatExporter/AppConfiguration.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppConfiguration.h 3 | // WechatExporter 4 | // 5 | // Created by Matthew on 2021/3/18. 6 | // Copyright © 2021 Matthew. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | 14 | typedef NS_ENUM(NSInteger, OUTPUT_FORMAT) 15 | { 16 | OUTPUT_FORMAT_HTML = 0, 17 | OUTPUT_FORMAT_TEXT, 18 | OUTPUT_FORMAT_PDF, 19 | OUTPUT_FORMAT_LAST 20 | }; 21 | 22 | @interface AppConfiguration : NSObject 23 | 24 | + (void)setDescOrder:(BOOL)descOrder; 25 | + (BOOL)getDescOrder; 26 | + (BOOL)IsPdfSupported; 27 | + (NSInteger)getOutputFormat; 28 | + (BOOL)isHtmlMode; 29 | + (BOOL)isTextMode; 30 | + (BOOL)isPdfMode; 31 | + (void)setOutputFormat:(NSInteger)outputFormat; 32 | + (void)setSavingInSession:(BOOL)savingInSession; 33 | + (BOOL)getSavingInSession; 34 | 35 | + (void)setSyncLoading; 36 | + (BOOL)getSyncLoading; 37 | 38 | + (void)setIncrementalExporting:(BOOL)incrementalExp; 39 | + (BOOL)getIncrementalExporting; 40 | 41 | + (void)setLastOutputDir:(NSString *)outputDir; 42 | + (NSString *)getLastOrDefaultOutputDir; 43 | + (NSString *)getDefaultOutputDir; 44 | 45 | + (void)setLastBackupDir:(NSString *)backupDir; 46 | + (NSString *)getLastBackupDir; 47 | + (NSString *)getDefaultBackupDir:(BOOL)checkExistence; // YES 48 | 49 | + (NSInteger)getLastCheckUpdateTime; 50 | + (void)setLastCheckUpdateTime; 51 | + (void)setLastCheckUpdateTime:(NSInteger)lastCheckUpdateTime; 52 | 53 | + (void)setCheckingUpdateDisabled:(BOOL)disabled; 54 | + (BOOL)isCheckingUpdateDisabled; 55 | 56 | + (void)setLoadingDataOnScroll; 57 | + (BOOL)getLoadingDataOnScroll; 58 | 59 | + (void)setNormalPagination; 60 | + (BOOL)getNormalPagination; 61 | + (void)setPaginationOnYear; 62 | + (BOOL)getPaginationOnYear; 63 | + (void)setPaginationOnMonth; 64 | + (BOOL)getPaginationOnMonth; 65 | 66 | + (void)setSupportingFilter:(BOOL)supportingFilter; 67 | + (BOOL)getSupportingFilter; 68 | 69 | + (void)setOutputDebugLogs:(BOOL)dbgLogs; 70 | + (BOOL)outputDebugLogs; 71 | 72 | + (void)setIncludingSubscriptions:(BOOL)includingSubscriptions; 73 | + (BOOL)includeSubscriptions; 74 | 75 | + (void)setOpenningFolderAfterExp:(BOOL)openningFolderAfterExp; 76 | + (BOOL)getOpenningFolderAfterExp; 77 | 78 | + (void)setSkipGuide:(BOOL)skipGuide; 79 | + (BOOL)getSkipGuide; 80 | 81 | + (void)upgrade; 82 | + (uint64_t)buildOptions; 83 | 84 | 85 | @end 86 | 87 | NS_ASSUME_NONNULL_END 88 | -------------------------------------------------------------------------------- /WechatExporter/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // WechatExporter 4 | // 5 | // Created by Matthew on 2020/9/29. 6 | // Copyright © 2020 Matthew. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : NSObject 12 | - (IBAction)fileMenuItemClick:(NSMenuItem *)sender; 13 | - (IBAction)formatMenuItemClick:(NSMenuItem *)sender; 14 | - (IBAction)optionsMenuItemClick:(NSMenuItem *)sender; 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /WechatExporter/Assets.xcassets/AppIcon.appiconset/1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/Assets.xcassets/AppIcon.appiconset/1024.png -------------------------------------------------------------------------------- /WechatExporter/Assets.xcassets/AppIcon.appiconset/128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/Assets.xcassets/AppIcon.appiconset/128.png -------------------------------------------------------------------------------- /WechatExporter/Assets.xcassets/AppIcon.appiconset/16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/Assets.xcassets/AppIcon.appiconset/16.png -------------------------------------------------------------------------------- /WechatExporter/Assets.xcassets/AppIcon.appiconset/256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/Assets.xcassets/AppIcon.appiconset/256.png -------------------------------------------------------------------------------- /WechatExporter/Assets.xcassets/AppIcon.appiconset/32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/Assets.xcassets/AppIcon.appiconset/32.png -------------------------------------------------------------------------------- /WechatExporter/Assets.xcassets/AppIcon.appiconset/512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/Assets.xcassets/AppIcon.appiconset/512.png -------------------------------------------------------------------------------- /WechatExporter/Assets.xcassets/AppIcon.appiconset/64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/Assets.xcassets/AppIcon.appiconset/64.png -------------------------------------------------------------------------------- /WechatExporter/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "16.png", 5 | "idiom" : "mac", 6 | "scale" : "1x", 7 | "size" : "16x16" 8 | }, 9 | { 10 | "filename" : "32.png", 11 | "idiom" : "mac", 12 | "scale" : "2x", 13 | "size" : "16x16" 14 | }, 15 | { 16 | "filename" : "32.png", 17 | "idiom" : "mac", 18 | "scale" : "1x", 19 | "size" : "32x32" 20 | }, 21 | { 22 | "filename" : "64.png", 23 | "idiom" : "mac", 24 | "scale" : "2x", 25 | "size" : "32x32" 26 | }, 27 | { 28 | "filename" : "128.png", 29 | "idiom" : "mac", 30 | "scale" : "1x", 31 | "size" : "128x128" 32 | }, 33 | { 34 | "filename" : "256.png", 35 | "idiom" : "mac", 36 | "scale" : "2x", 37 | "size" : "128x128" 38 | }, 39 | { 40 | "filename" : "256.png", 41 | "idiom" : "mac", 42 | "scale" : "1x", 43 | "size" : "256x256" 44 | }, 45 | { 46 | "filename" : "512.png", 47 | "idiom" : "mac", 48 | "scale" : "2x", 49 | "size" : "256x256" 50 | }, 51 | { 52 | "filename" : "512.png", 53 | "idiom" : "mac", 54 | "scale" : "1x", 55 | "size" : "512x512" 56 | }, 57 | { 58 | "filename" : "1024.png", 59 | "idiom" : "mac", 60 | "scale" : "2x", 61 | "size" : "512x512" 62 | } 63 | ], 64 | "info" : { 65 | "author" : "xcode", 66 | "version" : 1 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /WechatExporter/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /WechatExporter/Assets.xcassets/MainMenuCN.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "main-menu-cn@1x.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "main-menu-cn@2x.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "main-menu-cn@3x.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /WechatExporter/Assets.xcassets/MainMenuCN.imageset/main-menu-cn@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/Assets.xcassets/MainMenuCN.imageset/main-menu-cn@1x.png -------------------------------------------------------------------------------- /WechatExporter/Assets.xcassets/MainMenuCN.imageset/main-menu-cn@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/Assets.xcassets/MainMenuCN.imageset/main-menu-cn@2x.png -------------------------------------------------------------------------------- /WechatExporter/Assets.xcassets/MainMenuCN.imageset/main-menu-cn@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/Assets.xcassets/MainMenuCN.imageset/main-menu-cn@3x.png -------------------------------------------------------------------------------- /WechatExporter/Assets.xcassets/MainMenuEN.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "main-menu-en@1x.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "main-menu-en@2x.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "main-menu-en@3x.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /WechatExporter/Assets.xcassets/MainMenuEN.imageset/main-menu-en@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/Assets.xcassets/MainMenuEN.imageset/main-menu-en@1x.png -------------------------------------------------------------------------------- /WechatExporter/Assets.xcassets/MainMenuEN.imageset/main-menu-en@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/Assets.xcassets/MainMenuEN.imageset/main-menu-en@2x.png -------------------------------------------------------------------------------- /WechatExporter/Assets.xcassets/MainMenuEN.imageset/main-menu-en@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/Assets.xcassets/MainMenuEN.imageset/main-menu-en@3x.png -------------------------------------------------------------------------------- /WechatExporter/ExportNotifierImpl.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ExportNotifier.h" 4 | #include "ViewController.h" 5 | 6 | class ExportNotifierImpl : public ExportNotifier 7 | { 8 | protected: 9 | __weak ViewController *m_viewController; 10 | 11 | 12 | public: 13 | ExportNotifierImpl(ViewController* viewController) 14 | { 15 | m_viewController = viewController; 16 | } 17 | 18 | ~ExportNotifierImpl() 19 | { 20 | m_viewController = nil; 21 | } 22 | 23 | void onStart() const 24 | { 25 | __block __weak ViewController* viewController = m_viewController; 26 | dispatch_async(dispatch_get_main_queue(), ^{ 27 | __strong __typeof(viewController)strongVC = viewController; 28 | if (strongVC) 29 | { 30 | [strongVC onStart]; 31 | strongVC = nil; 32 | } 33 | }); 34 | } 35 | 36 | void onProgress(uint32_t numberOfMessages, uint32_t numberOfTotalMessages) const 37 | { 38 | } 39 | 40 | void onComplete(bool cancelled) const 41 | { 42 | __block __weak ViewController* viewController = m_viewController; 43 | __block BOOL localCancelled = cancelled; 44 | 45 | dispatch_async(dispatch_get_main_queue(), ^{ 46 | __strong __typeof(viewController)strongVC = viewController; 47 | if (strongVC) 48 | { 49 | [strongVC onComplete:localCancelled]; 50 | strongVC = nil; 51 | } 52 | }); 53 | } 54 | 55 | void onUserSessionStart(const std::string& usrName, uint32_t numberOfSessions) const 56 | { 57 | 58 | } 59 | 60 | void onUserSessionComplete(const std::string& usrName) const 61 | { 62 | 63 | } 64 | 65 | void onSessionStart(const std::string& sessionUsrName, void * sessionData, uint32_t numberOfTotalMessages) const 66 | { 67 | __block __weak ViewController* viewController = m_viewController; 68 | __block NSInteger row = (NSInteger)sessionData; 69 | __block NSString *usrName = [NSString stringWithUTF8String:sessionUsrName.c_str()]; 70 | dispatch_async(dispatch_get_main_queue(), ^{ 71 | __strong __typeof(viewController)strongVC = viewController; 72 | if (strongVC) 73 | { 74 | [strongVC onSessionStart:usrName row:row]; 75 | strongVC = nil; 76 | } 77 | }); 78 | } 79 | 80 | void onSessionProgress(const std::string& sessionUsrName, void * sessionData, uint32_t numberOfMessages, uint32_t numberOfTotalMessages) const 81 | { 82 | __block __weak ViewController* viewController = m_viewController; 83 | __block NSInteger row = (NSInteger)sessionData; 84 | __block NSUInteger numberOfMsgs = (NSUInteger)numberOfMessages; 85 | __block NSUInteger numberOfTotalMsgs = (NSUInteger)numberOfTotalMessages; 86 | __block NSString *usrName = [NSString stringWithUTF8String:sessionUsrName.c_str()]; 87 | dispatch_async(dispatch_get_main_queue(), ^{ 88 | __strong __typeof(viewController)strongVC = viewController; 89 | if (strongVC) 90 | { 91 | [strongVC onSessionProgress:usrName row:row numberOfMessages:numberOfMsgs numberOfTotalMessages:numberOfTotalMsgs]; 92 | strongVC = nil; 93 | } 94 | }); 95 | } 96 | 97 | void onSessionComplete(const std::string& sessionUsrName, void * sessionData, bool cancelled) const 98 | { 99 | __block __weak ViewController* viewController = m_viewController; 100 | // __block BOOL localCancelled = cancelled; 101 | __block NSString *usrName = [NSString stringWithUTF8String:sessionUsrName.c_str()]; 102 | dispatch_async(dispatch_get_main_queue(), ^{ 103 | __strong __typeof(viewController)strongVC = viewController; 104 | if (strongVC) 105 | { 106 | [strongVC onSessionComplete:usrName]; 107 | strongVC = nil; 108 | } 109 | }); 110 | } 111 | 112 | void onTasksStart(const std::string& usrName, uint32_t numberOfTotalTasks) const 113 | { 114 | 115 | } 116 | 117 | void onTasksProgress(const std::string& usrName, uint32_t numberOfCompletedTasks, uint32_t numberOfTotalMessages) const 118 | { 119 | 120 | } 121 | 122 | void onTasksComplete(const std::string& usrName, bool cancelled) const 123 | { 124 | 125 | } 126 | 127 | }; 128 | 129 | -------------------------------------------------------------------------------- /WechatExporter/HttpHelper.h: -------------------------------------------------------------------------------- 1 | // 2 | // HttpHelper.h 3 | // WechatExporter 4 | // 5 | // Created by Matthew on 2021/3/9. 6 | // Copyright © 2021 Matthew. All rights reserved. 7 | // 8 | 9 | #ifndef HttpHelper_h 10 | #define HttpHelper_h 11 | 12 | #import 13 | 14 | @interface HttpHelper : NSObject 15 | 16 | + (NSString *)standardUserAgent; 17 | 18 | @end 19 | 20 | #endif /* HttpHelper_h */ 21 | -------------------------------------------------------------------------------- /WechatExporter/HttpHelper.mm: -------------------------------------------------------------------------------- 1 | // 2 | // HttpHelper.m 3 | // WechatExporter 4 | // 5 | // Created by Matthew on 2021/3/9. 6 | // Copyright © 2021 Matthew. All rights reserved. 7 | // 8 | 9 | #import "HttpHelper.h" 10 | 11 | #if defined(__ppc__) || defined(__ppc64__) 12 | #define PROCESSOR "PPC" 13 | #elif defined(__i386__) || defined(__x86_64__) 14 | #define PROCESSOR "Intel" 15 | #else 16 | #error Unknown architecture 17 | #endif 18 | 19 | @implementation HttpHelper 20 | 21 | static inline int callGestalt(OSType selector) 22 | { 23 | SInt32 value = 0; 24 | Gestalt(selector, &value); 25 | return value; 26 | } 27 | 28 | // Uses underscores instead of dots because if "4." ever appears in a user agent string, old DHTML libraries treat it as Netscape 4. 29 | + (NSString *)macOSXVersionString 30 | { 31 | // Can't use -[NSProcessInfo operatingSystemVersionString] because it has too much stuff we don't want. 32 | int major = callGestalt(gestaltSystemVersionMajor); 33 | // ASSERT(major); 34 | 35 | int minor = callGestalt(gestaltSystemVersionMinor); 36 | int bugFix = callGestalt(gestaltSystemVersionBugFix); 37 | if (bugFix) 38 | return [NSString stringWithFormat:@"%d_%d_%d", major, minor, bugFix]; 39 | if (minor) 40 | return [NSString stringWithFormat:@"%d_%d", major, minor]; 41 | return [NSString stringWithFormat:@"%d", major]; 42 | } 43 | 44 | + (NSString *)userVisibleWebKitVersionString 45 | { 46 | // If the version is 4 digits long or longer, then the first digit represents 47 | // the version of the OS. Our user agent string should not include this first digit, 48 | // so strip it off and report the rest as the version. 49 | NSString *fullVersion = [[NSBundle bundleForClass:NSClassFromString(@"WKView")] objectForInfoDictionaryKey:(NSString *)kCFBundleVersionKey]; 50 | NSRange nonDigitRange = [fullVersion rangeOfCharacterFromSet:[[NSCharacterSet decimalDigitCharacterSet] invertedSet]]; 51 | if (nonDigitRange.location == NSNotFound && [fullVersion length] >= 4) 52 | return [fullVersion substringFromIndex:1]; 53 | if (nonDigitRange.location != NSNotFound && nonDigitRange.location >= 4) 54 | return [fullVersion substringFromIndex:1]; 55 | return fullVersion; 56 | } 57 | 58 | + (NSString *)standardUserAgent 59 | { 60 | // https://opensource.apple.com/source/WebKit2/WebKit2-7536.26.14/UIProcess/mac/WebPageProxyMac.mm.auto.html 61 | NSString *osVersion = [self macOSXVersionString]; 62 | NSString *webKitVersion = [self userVisibleWebKitVersionString]; 63 | 64 | return [NSString stringWithFormat:@"Mozilla/5.0 (Macintosh; %@ Mac OS X %@) AppleWebKit/%@ (KHTML, like Gecko) ", @PROCESSOR, osVersion, webKitVersion]; 65 | } 66 | 67 | 68 | @end 69 | -------------------------------------------------------------------------------- /WechatExporter/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 19 | CFBundleShortVersionString 20 | $(MARKETING_VERSION) 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | LSApplicationCategoryType 24 | public.app-category.utilities 25 | LSMinimumSystemVersion 26 | $(MACOSX_DEPLOYMENT_TARGET) 27 | NSHumanReadableCopyright 28 | Copyright © 2020 Matthew. All rights reserved. 29 | NSMainStoryboardFile 30 | Main 31 | NSPrincipalClass 32 | NSApplication 33 | NSSupportsAutomaticTermination 34 | 35 | NSSupportsSuddenTermination 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /WechatExporter/LICENSES/jsoncpp.LICENSE: -------------------------------------------------------------------------------- 1 | The JsonCpp library's source code, including accompanying documentation, 2 | tests and demonstration applications, are licensed under the following 3 | conditions... 4 | 5 | Baptiste Lepilleur and The JsonCpp Authors explicitly disclaim copyright in all 6 | jurisdictions which recognize such a disclaimer. In such jurisdictions, 7 | this software is released into the Public Domain. 8 | 9 | In jurisdictions which do not recognize Public Domain property (e.g. Germany as of 10 | 2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur and 11 | The JsonCpp Authors, and is released under the terms of the MIT License (see below). 12 | 13 | In jurisdictions which recognize Public Domain property, the user of this 14 | software may choose to accept it either as 1) Public Domain, 2) under the 15 | conditions of the MIT License (see below), or 3) under the terms of dual 16 | Public Domain/MIT License conditions described here, as they choose. 17 | 18 | The MIT License is about as close to Public Domain as a license can get, and is 19 | described in clear, concise terms at: 20 | 21 | http://en.wikipedia.org/wiki/MIT_License 22 | 23 | The full text of the MIT License follows: 24 | 25 | ======================================================================== 26 | Copyright (c) 2007-2010 Baptiste Lepilleur and The JsonCpp Authors 27 | 28 | Permission is hereby granted, free of charge, to any person 29 | obtaining a copy of this software and associated documentation 30 | files (the "Software"), to deal in the Software without 31 | restriction, including without limitation the rights to use, copy, 32 | modify, merge, publish, distribute, sublicense, and/or sell copies 33 | of the Software, and to permit persons to whom the Software is 34 | furnished to do so, subject to the following conditions: 35 | 36 | The above copyright notice and this permission notice shall be 37 | included in all copies or substantial portions of the Software. 38 | 39 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 40 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 41 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 42 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 43 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 44 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 45 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 46 | SOFTWARE. 47 | ======================================================================== 48 | (END LICENSE TEXT) 49 | 50 | The MIT license is compatible with both the GPL and commercial 51 | software, affording one all of the rights of Public Domain with the 52 | minor nuisance of being required to keep the above copyright notice 53 | and license text in the source code. Note also that by accepting the 54 | Public Domain "license" you can re-license your copy using whatever 55 | license you like. 56 | -------------------------------------------------------------------------------- /WechatExporter/LICENSES/lame.LICENSE: -------------------------------------------------------------------------------- 1 | Can I use LAME in my commercial program? 2 | 3 | Yes, you can, under the restrictions of the LGPL (see COPYING 4 | in this folder). The easiest way to do this is to: 5 | 6 | 1. Link to LAME as separate library (libmp3lame.a on unix or 7 | lame_enc.dll or libmp3lame.dll on windows) 8 | 9 | 2. Fully acknowledge that you are using LAME, and give a link 10 | to our web site, www.mp3dev.org 11 | 12 | 3. If you make modifications to LAME, you *must* release these 13 | modifications back to the LAME project, under the LGPL. 14 | -------------------------------------------------------------------------------- /WechatExporter/LICENSES/libcurl.COPYING: -------------------------------------------------------------------------------- 1 | COPYRIGHT AND PERMISSION NOTICE 2 | 3 | Copyright (c) 1996 - 2020, Daniel Stenberg, , and many 4 | contributors, see the THANKS file. 5 | 6 | All rights reserved. 7 | 8 | Permission to use, copy, modify, and distribute this software for any purpose 9 | with or without fee is hereby granted, provided that the above copyright 10 | notice and this permission notice appear in all copies. 11 | 12 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 13 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 14 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN 15 | NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 16 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 17 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 18 | OR OTHER DEALINGS IN THE SOFTWARE. 19 | 20 | Except as contained in this notice, the name of a copyright holder shall not 21 | be used in advertising or otherwise to promote the sale, use or other dealings 22 | in this Software without prior written authorization of the copyright holder. 23 | -------------------------------------------------------------------------------- /WechatExporter/LICENSES/libxml2.Copyright: -------------------------------------------------------------------------------- 1 | Except where otherwise noted in the source code (e.g. the files hash.c, 2 | list.c and the trio files, which are covered by a similar licence but 3 | with different Copyright notices) all the files are: 4 | 5 | Copyright (C) 1998-2012 Daniel Veillard. All Rights Reserved. 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is fur- 12 | nished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in 15 | all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FIT- 19 | NESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /WechatExporter/LICENSES/protobuf.LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2008 Google Inc. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | Code generated by the Protocol Buffer compiler is owned by the owner 30 | of the input file used when generating it. This code is not 31 | standalone and requires a support library to be linked with it. This 32 | support library is itself covered by the above license. 33 | -------------------------------------------------------------------------------- /WechatExporter/LoggerImpl.h: -------------------------------------------------------------------------------- 1 | // 2 | // LoggerImpl.h 3 | // WechatExporter 4 | // 5 | // Created by Matthew on 2020/10/1. 6 | // Copyright © 2020 Matthew. All rights reserved. 7 | // 8 | 9 | #include "core/Logger.h" 10 | #include 11 | #include "ViewController.h" 12 | 13 | #ifndef LoggerImpl_h 14 | #define LoggerImpl_h 15 | 16 | class LoggerImpl : public Logger 17 | { 18 | protected: 19 | __weak ViewController *m_viewController; 20 | NSLock *m_lock; 21 | char m_logFile[1024]; 22 | 23 | public: 24 | LoggerImpl(ViewController* viewController) 25 | { 26 | m_viewController = viewController; 27 | m_lock = [[NSLock alloc] init]; 28 | } 29 | 30 | ~LoggerImpl() 31 | { 32 | m_viewController = nil; 33 | m_lock = nil; 34 | } 35 | 36 | void setLogPath(const char* logPath) 37 | { 38 | #if !defined(NDEBUG) || defined(DBG_PERF) 39 | [m_lock lock]; 40 | strcpy(m_logFile, logPath); 41 | if (!endsWith(logPath, DIR_SEP_STR)) 42 | { 43 | strcat(m_logFile, DIR_SEP_STR); 44 | } 45 | strcat(m_logFile, "log.txt"); 46 | FILE *file = fopen(m_logFile, "w"); 47 | if (NULL != file) 48 | { 49 | fclose(file); 50 | } 51 | [m_lock unlock]; 52 | #endif 53 | } 54 | 55 | void write(const std::string& log) 56 | { 57 | #if !defined(NDEBUG) || defined(DBG_PERF) 58 | std::string timeString = getTimestampString(false, true) + ": "; 59 | #else 60 | std::string timeString = getTimestampString() + ": "; 61 | #endif 62 | 63 | #if !defined(NDEBUG) || defined(DBG_PERF) 64 | [m_lock lock]; 65 | if (strlen(m_logFile) > 0) 66 | { 67 | FILE *file = fopen(m_logFile, "a"); 68 | if (NULL != file) 69 | { 70 | fputs(timeString.c_str(), file); 71 | fputs(log.c_str(), file); 72 | fputs("\r", file); 73 | fclose(file); 74 | } 75 | } 76 | [m_lock unlock]; 77 | #endif 78 | 79 | __block NSString *logString = [NSString stringWithUTF8String:(timeString + log).c_str()]; 80 | __block __weak ViewController* viewController = m_viewController; 81 | dispatch_async(dispatch_get_main_queue(), ^{ 82 | __strong __typeof(viewController)strongVC = viewController; 83 | if (strongVC) 84 | { 85 | [strongVC writeLog:logString]; 86 | strongVC = nil; 87 | } 88 | }); 89 | } 90 | 91 | void debug(const std::string& log) 92 | { 93 | write(log); 94 | #if !defined(NDEBUG) || defined(DBG_PERF) 95 | NSString *logString = [NSString stringWithUTF8String:log.c_str()]; 96 | NSLog(@"%@", logString); 97 | #endif 98 | } 99 | 100 | }; 101 | 102 | #endif /* LoggerImpl_h */ 103 | -------------------------------------------------------------------------------- /WechatExporter/SessionDataSource.h: -------------------------------------------------------------------------------- 1 | // 2 | // SessionDataSource.h 3 | // WechatExporter 4 | // 5 | // Created by Matthew on 2021/2/1. 6 | // Copyright © 2021 Matthew. All rights reserved. 7 | // 8 | 9 | #ifndef SessionDataSource_h 10 | #define SessionDataSource_h 11 | 12 | #import 13 | #include 14 | #include 15 | #include 16 | 17 | #import "WechatObjects.h" 18 | 19 | @interface SessionItem : NSObject 20 | 21 | @property (assign) NSInteger orgIndex; 22 | @property (assign) NSInteger userIndex; 23 | @property (assign) BOOL checked; 24 | @property (strong) NSString *sessionUsrName; 25 | @property (strong) NSString *displayName; 26 | @property (assign) NSInteger recordCount; 27 | @property (strong) NSString *userDisplayName; 28 | @property (strong) NSString *usrName; 29 | @property (strong) NSString *lastMessage; 30 | #ifndef NDEBUG 31 | @property (assign) NSUInteger lastMessageTime; 32 | #endif 33 | // @property (assign) NSInteger userPointer; 34 | // @property (assign) NSInteger sessionPointer; 35 | 36 | // - (NSComparisonResult)orgIndexCompare:(SessionItem *)sessionItem:(BOOL)ascending; 37 | - (NSComparisonResult)displayNameCompare:(SessionItem *)sessionItem ascending:(BOOL)ascending; 38 | - (NSComparisonResult)recordCountCompare:(SessionItem *)sessionItem ascending:(BOOL)ascending; 39 | - (NSComparisonResult)userIndexCompare:(SessionItem *)sessionItem ascending:(BOOL)ascending; 40 | 41 | 42 | @end 43 | 44 | @interface SessionDataSource : NSObject 45 | 46 | @property (nonatomic, assign) NSInteger rowInProgress; 47 | @property (nonatomic, assign) NSUInteger numberOfMsgExported; 48 | 49 | - (void)loadData:(const std::vector>> *)usersAndSessions withAllUsers:(BOOL)allUsers indexOfSelectedUser:(NSInteger)indexOfSelectedUser includesSubscription:(BOOL)includesSubscriptions; 50 | - (void)getSelectedUserAndSessions:(std::map>&)usersAndSessions; 51 | 52 | - (void)bindCellView:(NSTableCellView *)cellView atRow:(NSInteger)row andColumnId:(NSString *)identifier; 53 | - (NSControlStateValue)updateCheckStateAtRow:(NSInteger)row; 54 | - (void)checkAllSessions:(BOOL)checked; 55 | 56 | @end 57 | 58 | #endif /* SessionDataSource_h */ 59 | -------------------------------------------------------------------------------- /WechatExporter/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // WechatExporter 4 | // 5 | // Created by Matthew on 2020/9/29. 6 | // Copyright © 2020 Matthew. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : NSViewController 12 | 13 | @property (weak) IBOutlet NSTextField *lblITunes; 14 | @property (weak) IBOutlet NSButton *btnExport; 15 | @property (weak) IBOutlet NSButton *btnCancel; 16 | @property (weak) IBOutlet NSButton *btnQuit; 17 | 18 | @property (weak) IBOutlet NSTextField *txtboxOutput; 19 | @property (weak) IBOutlet NSPopUpButton *popupBackup; 20 | @property (weak) IBOutlet NSPopUpButton *popupUsers; 21 | @property (weak) IBOutlet NSButton *btnToggleAll; 22 | @property (weak) IBOutlet NSScrollView *sclSessions; 23 | @property (weak) IBOutlet NSTableView *tblSessions; 24 | 25 | @property (weak) IBOutlet NSButton *btnBackup; 26 | @property (weak) IBOutlet NSButton *btnOutput; 27 | @property (weak) IBOutlet NSButton *btnShowLogs; 28 | @property (weak) IBOutlet NSButton *btnBackupDevice; 29 | 30 | @property (weak) IBOutlet NSProgressIndicator *progressBar; 31 | @property (weak) IBOutlet NSScrollView *sclViewLogs; 32 | @property (unsafe_unretained) IBOutlet NSTextView *txtViewLogs; 33 | 34 | - (void)writeLog:(NSString *)log; 35 | - (void)onStart; 36 | - (void)onComplete:(BOOL)cancelled; 37 | 38 | - (void)onSessionStart:(NSString *)usrName row:(NSInteger)row; 39 | - (void)onSessionProgress:(NSString *)sessionUsrName row:(NSInteger)row numberOfMessages:(NSUInteger)numberOfMessages numberOfTotalMessages:(NSUInteger)numberOfTotalMessages; 40 | - (void)onSessionComplete:(NSString *)usrName; 41 | 42 | @end 43 | 44 | -------------------------------------------------------------------------------- /WechatExporter/WechatExporter.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.assets.pictures.read-only 8 | 9 | com.apple.security.cs.disable-library-validation 10 | 11 | com.apple.security.files.downloads.read-only 12 | 13 | com.apple.security.files.user-selected.read-write 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /WechatExporter/core/AsyncExecutor.h: -------------------------------------------------------------------------------- 1 | // 2 | // AsyncExecutor.h 3 | // WechatExporter 4 | // 5 | // Created by Matthew on 2021/4/17. 6 | // Copyright © 2021 Matthew. All rights reserved. 7 | // 8 | 9 | #ifndef AsyncExecutor_h 10 | #define AsyncExecutor_h 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | class AsyncExecutor 20 | { 21 | public: 22 | 23 | class Task 24 | { 25 | public: 26 | virtual bool run() = 0; 27 | virtual int getType() const = 0; 28 | virtual std::string getName() const 29 | { 30 | return ""; 31 | } 32 | virtual bool hasError() const 33 | { 34 | return false; 35 | } 36 | virtual std::string getError() const 37 | { 38 | return ""; 39 | } 40 | 41 | Task() : m_taskId(0u), m_userData(NULL) 42 | { 43 | } 44 | virtual ~Task() {} 45 | 46 | uint32_t getTaskId() const 47 | { 48 | return m_taskId; 49 | } 50 | 51 | void setTaskId(uint32_t taskId) 52 | { 53 | m_taskId = taskId; 54 | } 55 | 56 | const void* getUserData() const 57 | { 58 | return m_userData; 59 | } 60 | 61 | void setUserData(const void* userData) 62 | { 63 | m_userData = userData; 64 | } 65 | 66 | private: 67 | uint32_t m_taskId; 68 | const void* m_userData; 69 | }; 70 | 71 | class Callback 72 | { 73 | public: 74 | virtual void onTaskStart(const AsyncExecutor* executor, const Task *task) = 0; 75 | virtual void onTaskComplete(const AsyncExecutor* executor, const Task *task, bool succeeded) = 0; 76 | virtual ~Callback() {} 77 | }; 78 | 79 | protected: 80 | class Thread 81 | { 82 | public: 83 | Thread(AsyncExecutor* executor); 84 | ~Thread(); 85 | private: 86 | AsyncExecutor* m_executor; 87 | std::unique_ptr m_thread; 88 | void ThreadFunc(); 89 | 90 | }; 91 | 92 | public: 93 | explicit AsyncExecutor(int reserve_threads, int max_threads, Callback *callback); 94 | ~AsyncExecutor(); 95 | 96 | static uint32_t genNextTaskId(); 97 | void addTask(Task *task); 98 | 99 | size_t getNumberOfQueue() const; 100 | void shutdown(); 101 | void cancel(); 102 | // true: completed, false: timeout 103 | bool waitForCompltion(unsigned int ms); 104 | 105 | #if !defined(NDEBUG) || defined(DBG_PERF) 106 | void setTag(const std::string& tag) 107 | { 108 | m_tag = tag; 109 | } 110 | #endif 111 | 112 | protected: 113 | 114 | Callback* m_callback; 115 | #if !defined(NDEBUG) || defined(DBG_PERF) 116 | std::string m_tag; 117 | uint32_t m_tid; 118 | #endif 119 | 120 | mutable std::mutex m_mutex; 121 | std::condition_variable m_cv; 122 | std::condition_variable m_shutdown_cv; 123 | bool m_shutdown; 124 | std::queue m_tasks; 125 | int m_reserve_threads; 126 | int m_max_threads; 127 | int m_nthreads; 128 | 129 | int m_threads_waiting; 130 | std::list m_dead_threads; 131 | 132 | static std::atomic_uint32_t m_nextTaskId; 133 | 134 | void ThreadFunc(); 135 | static void DestroyThreads(std::list* m_threads); 136 | 137 | }; 138 | 139 | #endif /* AsyncExecutor_h */ 140 | -------------------------------------------------------------------------------- /WechatExporter/core/ByteArrayLocater.h: -------------------------------------------------------------------------------- 1 | // 2 | // ByteArrayLocater.h 3 | // WechatExporter 4 | // 5 | // Created by Matthew on 2020/10/19. 6 | // Copyright © 2020 Matthew. All rights reserved. 7 | // 8 | 9 | #include 10 | 11 | #ifndef ByteArrayLocater_h 12 | #define ByteArrayLocater_h 13 | 14 | class ByteArrayLocater 15 | { 16 | public: 17 | std::vector locate(const unsigned char* data, int length, const unsigned char* candidate, int candidateLength) 18 | { 19 | std::vector positions; 20 | if (isEmptyLocate(data, length, candidate, candidateLength)) 21 | return positions; 22 | 23 | for (int i = 0; i < length; i++) 24 | { 25 | if (!isMatch(data, length, i, candidate, candidateLength)) 26 | continue; 27 | 28 | positions.push_back(i); 29 | } 30 | 31 | return positions; 32 | } 33 | 34 | std::vector> locatePair(const unsigned char* data, int length, const unsigned char* startCandidate, int startCandidateLength, const unsigned char* endCandidate, int endCandidateLength) 35 | { 36 | std::vector> positions; 37 | if (isEmptyLocate(data, length, startCandidate, startCandidateLength)) 38 | return positions; 39 | 40 | for (int i = 0; i < length;) 41 | { 42 | if (!isMatch(data, length, i, startCandidate, startCandidateLength)) 43 | { 44 | i++; 45 | continue; 46 | } 47 | 48 | int j = i + startCandidateLength; 49 | 50 | for (; j < length;) 51 | { 52 | if (!isMatch(data, length, j, endCandidate, endCandidateLength)) 53 | { 54 | j++; 55 | continue; 56 | } 57 | 58 | positions.push_back(std::make_pair(i, j)); 59 | break; 60 | } 61 | 62 | if (j == length) 63 | { 64 | // No endTag found 65 | break; 66 | } 67 | 68 | i = j + endCandidateLength; 69 | } 70 | 71 | return positions; 72 | } 73 | 74 | bool isMatch(const unsigned char* data, int length, int position, const unsigned char* candidate, int candidateLength) 75 | { 76 | if (candidateLength > (length - position)) 77 | return false; 78 | 79 | for (int i = 0; i < candidateLength; i++) 80 | if (data[position + i] != candidate[i]) 81 | return false; 82 | 83 | return true; 84 | } 85 | 86 | bool isEmptyLocate(const unsigned char* data, int length, const unsigned char* candidate, int candidateLength) 87 | { 88 | return data == NULL 89 | || candidate == NULL 90 | || length == 0 91 | || candidateLength == 0 92 | || candidateLength > length; 93 | } 94 | 95 | 96 | }; 97 | 98 | #endif /* ByteArrayLocater_h */ 99 | -------------------------------------------------------------------------------- /WechatExporter/core/DownloadPool.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // DownloadPool.cpp 3 | // WechatExporter 4 | // 5 | // Created by Matthew on 2020/9/30. 6 | // Copyright © 2020 Matthew. All rights reserved. 7 | // 8 | 9 | #include "DownloadPool.h" 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include "OSDef.h" 16 | 17 | size_t writeData(void *buffer, size_t size, size_t nmemb, void *user_p) 18 | { 19 | Task *task = reinterpret_cast(user_p); 20 | if (NULL != task) 21 | { 22 | return task->writeData(buffer, size, nmemb); 23 | } 24 | 25 | return 0; 26 | } 27 | 28 | void Task::run() 29 | { 30 | std::ofstream output(m_output, std::fstream::in | std::fstream::out | std::fstream::trunc); 31 | output.close(); 32 | 33 | CURL *curl_handler = curl_easy_init(); 34 | curl_easy_setopt(curl_handler, CURLOPT_URL, m_url.c_str()); 35 | curl_easy_setopt(curl_handler, CURLOPT_TIMEOUT, 60); 36 | curl_easy_setopt(curl_handler, CURLOPT_WRITEFUNCTION, &::writeData); 37 | curl_easy_setopt(curl_handler, CURLOPT_WRITEDATA, this); 38 | 39 | curl_easy_perform(curl_handler); 40 | 41 | curl_easy_cleanup(curl_handler); 42 | } 43 | 44 | size_t Task::writeData(void *buffer, size_t size, size_t nmemb) 45 | { 46 | std::ofstream file; 47 | file.open (m_output, std::fstream::in | std::fstream::out | std::fstream::app | std::fstream::binary); 48 | // file.write(buffer, size); 49 | size_t bytesToWrite = size * nmemb; 50 | file.write(reinterpret_cast(buffer), bytesToWrite); 51 | file.close(); 52 | 53 | return bytesToWrite; 54 | } 55 | 56 | DownloadPool::DownloadPool() 57 | { 58 | m_noMoreTask = false; 59 | curl_global_init(CURL_GLOBAL_ALL); 60 | 61 | for (int idx = 0; idx < 4; idx++) 62 | { 63 | m_threads.push_back(std::thread(&DownloadPool::run, this)); 64 | } 65 | // vecOfThreads.push_back(std::thread(func)); 66 | 67 | } 68 | 69 | DownloadPool::~DownloadPool() 70 | { 71 | curl_global_cleanup(); 72 | } 73 | 74 | void DownloadPool::addTask(const std::string &url, const std::string& output) 75 | { 76 | std::string formatedPath = output; 77 | std::replace(formatedPath.begin(), formatedPath.end(), DIR_SEP_R, DIR_SEP); 78 | #ifndef NDEBUG 79 | struct stat buffer; 80 | if (stat (formatedPath.c_str(), &buffer) == 0) 81 | { 82 | return; 83 | } 84 | #endif 85 | 86 | std::string uid = url + output; 87 | bool existed = false; 88 | Task task(url, formatedPath); 89 | m_mtx.lock(); 90 | if (!(existed = (m_urls.find(uid) != m_urls.cend()))) 91 | { 92 | m_urls.insert(uid); 93 | m_queue.push(task); 94 | } 95 | m_mtx.unlock(); 96 | 97 | if (existed) 98 | { 99 | #ifndef NDEBUG 100 | printf("URL Existed: %s", url.c_str()); 101 | #endif 102 | } 103 | } 104 | 105 | void DownloadPool::setNoMoreTask() 106 | { 107 | m_mtx.lock(); 108 | m_noMoreTask = true; 109 | m_mtx.unlock(); 110 | } 111 | 112 | void DownloadPool::run() 113 | { 114 | while(1) 115 | { 116 | bool found = false; 117 | bool noMoreTask = false; 118 | 119 | Task task; 120 | m_mtx.lock(); 121 | 122 | size_t queueSize = m_queue.size(); 123 | if (queueSize > 0) 124 | { 125 | task = m_queue.front(); 126 | m_queue.pop(); 127 | found = true; 128 | } 129 | 130 | noMoreTask = m_noMoreTask; 131 | 132 | m_mtx.unlock(); 133 | 134 | if (found) 135 | { 136 | // run task 137 | task.run(); 138 | continue; 139 | } 140 | if (m_noMoreTask) 141 | { 142 | break; 143 | } 144 | else 145 | { 146 | std::this_thread::sleep_for(std::chrono::milliseconds(100)); 147 | } 148 | } 149 | } 150 | 151 | void DownloadPool::finishAndWaitForExit() 152 | { 153 | setNoMoreTask(); 154 | for (std::vector::iterator it = m_threads.begin(); it != m_threads.end(); ++it) 155 | { 156 | it->join(); 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /WechatExporter/core/DownloadPool.h: -------------------------------------------------------------------------------- 1 | // 2 | // DownloadPool.h 3 | // WechatExporter 4 | // 5 | // Created by Matthew on 2020/9/30. 6 | // Copyright © 2020 Matthew. All rights reserved. 7 | // 8 | 9 | #ifndef DownloadPool_h 10 | #define DownloadPool_h 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | class Task 21 | { 22 | protected: 23 | std::string m_url; 24 | std::string m_output; 25 | 26 | public: 27 | Task() 28 | { 29 | } 30 | 31 | Task(const std::string &url, const std::string& output) 32 | { 33 | m_url = url; 34 | m_output = output; 35 | } 36 | 37 | Task& operator=(const Task& task) 38 | { 39 | if (this != &task) 40 | { 41 | m_url = task.m_url; 42 | m_output = task.m_output; 43 | } 44 | 45 | return *this; 46 | } 47 | 48 | size_t writeData(void *buffer, size_t size, size_t nmemb); 49 | void run(); 50 | }; 51 | 52 | class DownloadPool 53 | { 54 | protected: 55 | std::queue m_queue; 56 | std::set m_urls; 57 | std::mutex m_mtx; 58 | bool m_noMoreTask; 59 | std::vector m_threads; 60 | 61 | public: 62 | DownloadPool(); 63 | ~DownloadPool(); 64 | 65 | void addTask(const std::string &url, const std::string& output); 66 | void setNoMoreTask(); 67 | void run(); 68 | 69 | void finishAndWaitForExit(); 70 | }; 71 | 72 | #endif /* DownloadPool_h */ 73 | -------------------------------------------------------------------------------- /WechatExporter/core/Downloader.h: -------------------------------------------------------------------------------- 1 | // 2 | // Downloader.h 3 | // WechatExporter 4 | // 5 | // Created by Matthew on 2020/9/30. 6 | // Copyright © 2020 Matthew. All rights reserved. 7 | // 8 | 9 | #ifndef Downloader_h 10 | #define Downloader_h 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include "Logger.h" 21 | 22 | #ifdef USING_DOWNLOADER 23 | 24 | class Task 25 | { 26 | protected: 27 | uint32_t m_taskId; 28 | std::string m_url; 29 | std::string m_output; 30 | std::string m_outputTmp; 31 | std::string m_error; 32 | std::string m_userAgent; 33 | time_t m_mtime; 34 | bool m_localCopy; 35 | unsigned int m_retries; 36 | public: 37 | static const unsigned int MAX_RETRIES = 3; 38 | public: 39 | Task() : m_taskId(0), m_localCopy(false) 40 | { 41 | } 42 | 43 | Task(uint32_t taskId, const std::string &url, const std::string& output, time_t mtime, bool localCopy = false); 44 | 45 | void setUserAgent(const std::string& userAgent) 46 | { 47 | m_userAgent = userAgent; 48 | } 49 | 50 | inline std::string getUrl() const 51 | { 52 | return m_url; 53 | } 54 | 55 | inline std::string getOutput() const 56 | { 57 | return m_output; 58 | } 59 | 60 | inline std::string getError() const 61 | { 62 | return m_error; 63 | } 64 | 65 | bool isLocalCopy() const 66 | { 67 | return m_localCopy; 68 | } 69 | 70 | Task& operator=(const Task& task) 71 | { 72 | if (this != &task) 73 | { 74 | m_taskId = task.m_taskId; 75 | m_url = task.m_url; 76 | m_output = task.m_output; 77 | m_mtime = task.m_mtime; 78 | m_localCopy = task.m_localCopy; 79 | m_retries = task.m_retries; 80 | } 81 | 82 | return *this; 83 | } 84 | 85 | size_t writeData(void *buffer, size_t size, size_t nmemb); 86 | bool run(); 87 | unsigned int getRetries() const; 88 | 89 | protected: 90 | bool downloadFile(); 91 | bool copyFile(); 92 | }; 93 | 94 | class Downloader 95 | { 96 | protected: 97 | std::queue m_queue; 98 | std::queue m_copyQueue; 99 | std::map m_urls; // url => local file path for first download 100 | mutable std::mutex m_mtx; 101 | bool m_noMoreTask; 102 | size_t m_downloadTaskSize; // +1 when task is added, -1 when download is completed 103 | std::vector m_threads; 104 | std::string m_userAgent; 105 | static std::atomic_uint32_t m_nextTaskId; 106 | 107 | Logger* m_logger; 108 | 109 | public: 110 | Downloader(Logger* logger); 111 | ~Downloader(); 112 | 113 | void setUserAgent(const std::string& userAgent); 114 | 115 | // return taskId 116 | uint32_t addTask(const std::string &url, const std::string& output, time_t mtime, std::string type = ""); 117 | void setNoMoreTask(); 118 | void run(int idx); 119 | 120 | void cancel(); 121 | void shutdown(); 122 | int getCount() const; 123 | int getRunningCount() const; 124 | 125 | static void initialize(); 126 | static void uninitialize(); 127 | 128 | static bool httpGet(const std::string& url, const std::vector>& headers, long& httpStatus, std::vector& body); 129 | 130 | #ifndef NDEBUG 131 | std::string getStats() const; 132 | #endif 133 | 134 | protected: 135 | const Task& dequeue(); 136 | 137 | 138 | #ifndef NDEBUG 139 | std::map m_statsType; 140 | #endif 141 | }; 142 | 143 | #endif // USING_DOWNLOADER 144 | 145 | #endif /* Downloader_h */ 146 | -------------------------------------------------------------------------------- /WechatExporter/core/ExportNotifier.h: -------------------------------------------------------------------------------- 1 | #ifndef ExportNotifier_h 2 | #define ExportNotifier_h 3 | 4 | class ExportNotifier 5 | { 6 | public: 7 | 8 | virtual ~ExportNotifier() {} 9 | 10 | virtual void onStart() const = 0; 11 | virtual void onProgress(uint32_t numberOfMessages, uint32_t numberOfTotalMessages) const = 0; 12 | virtual void onComplete(bool cancelled) const = 0; 13 | 14 | virtual void onUserSessionStart(const std::string& usrName, uint32_t numberOfSessions) const = 0; 15 | virtual void onUserSessionComplete(const std::string& usrName) const = 0; 16 | 17 | virtual void onSessionStart(const std::string& sessionUsrName, void * sessionData, uint32_t numberOfTotalMessages) const = 0; 18 | virtual void onSessionProgress(const std::string& sessionUsrName, void * sessionData, uint32_t numberOfMessages, uint32_t numberOfTotalMessages) const = 0; 19 | virtual void onSessionComplete(const std::string& sessionUsrName, void * sessionData, bool cancelled) const = 0; 20 | 21 | virtual void onTasksStart(const std::string& usrName, uint32_t numberOfTotalTasks) const = 0; 22 | virtual void onTasksProgress(const std::string& usrName, uint32_t numberOfCompletedTasks, uint32_t numberOfTotalMessages) const = 0; 23 | virtual void onTasksComplete(const std::string& usrName, bool cancelled) const = 0; 24 | 25 | }; 26 | 27 | #endif /* ExportNotifier_h */ 28 | -------------------------------------------------------------------------------- /WechatExporter/core/Logger.h: -------------------------------------------------------------------------------- 1 | // 2 | // Logger.h 3 | // WechatExporter 4 | // 5 | // Created by Matthew on 2020/9/30. 6 | // Copyright © 2020 Matthew. All rights reserved. 7 | // 8 | 9 | #include 10 | 11 | #ifndef Logger_h 12 | #define Logger_h 13 | 14 | class Logger 15 | { 16 | public: 17 | virtual void write(const std::string& log) = 0; 18 | virtual void debug(const std::string& log) = 0; 19 | virtual ~Logger() {} 20 | }; 21 | 22 | #endif /* Logger_h */ 23 | -------------------------------------------------------------------------------- /WechatExporter/core/MMKVReader.h: -------------------------------------------------------------------------------- 1 | // 2 | // MMKVReader.h 3 | // WechatExporter 4 | // 5 | // Created by Matthew on 2021/1/26. 6 | // Copyright © 2021 Matthew. All rights reserved. 7 | // 8 | 9 | #ifndef MMKVReader_h 10 | #define MMKVReader_h 11 | 12 | class MMKVReader 13 | { 14 | private: 15 | const unsigned char *m_ptr; 16 | size_t m_size; 17 | mutable size_t m_position; 18 | 19 | public: 20 | 21 | MMKVReader(const unsigned char *ptr, size_t size) : m_ptr(ptr), m_size(size), m_position(0) 22 | { 23 | } 24 | std::string readKey() const 25 | { 26 | std::string key; 27 | uint32_t keyLength = 0; 28 | const unsigned char* data = calcVarint32Ptr(m_ptr + m_position, m_ptr + m_size, &keyLength); 29 | m_position += data - (m_ptr + m_position); 30 | #if !defined(NDEBUG) 31 | assert(m_position <= m_size); 32 | #endif 33 | if (keyLength > 0) 34 | { 35 | auto s_size = static_cast(keyLength); 36 | if (s_size <= m_size - m_position) 37 | { 38 | key.assign((char *) (m_ptr + m_position), s_size); 39 | m_position += s_size; 40 | } 41 | else 42 | { 43 | m_position = m_size; 44 | } 45 | } 46 | 47 | #if !defined(NDEBUG) 48 | assert(m_position <= m_size); 49 | #endif 50 | 51 | return key; 52 | } 53 | 54 | void skipValue() const 55 | { 56 | uint32_t valueLength = 0; 57 | const unsigned char* data = calcVarint32Ptr(m_ptr + m_position, m_ptr + m_size, &valueLength); 58 | m_position += data - (m_ptr + m_position); 59 | if (valueLength > 0) 60 | { 61 | if ((m_position + valueLength) > m_size) 62 | { 63 | m_position = m_size; 64 | } 65 | else 66 | { 67 | m_position += valueLength; 68 | } 69 | } 70 | } 71 | 72 | std::string readStringValue() const 73 | { 74 | // MMBuffer 75 | std::string value; 76 | uint32_t valueLength = 0; 77 | const unsigned char* data = calcVarint32Ptr(m_ptr + m_position, m_ptr + m_size, &valueLength); 78 | m_position += data - (m_ptr + m_position); 79 | if (valueLength > 0) 80 | { 81 | // MMBuffer 82 | uint32_t mbbLength = 0; 83 | const unsigned char *ptr = m_ptr + m_position; 84 | ptr = calcVarint32Ptr(ptr, m_ptr + m_size, &mbbLength); 85 | #if !defined(NDEBUG) 86 | assert((m_position + valueLength) <= m_size); 87 | assert(valueLength == (ptr - (m_ptr + m_position)) + mbbLength); 88 | #endif 89 | if (mbbLength > 0) 90 | { 91 | auto s_size = static_cast(mbbLength); 92 | if (s_size <= m_size - m_position) 93 | { 94 | value.assign((char *)(ptr), s_size); 95 | m_position += valueLength; 96 | } 97 | else 98 | { 99 | m_position = m_size; 100 | } 101 | } 102 | else 103 | { 104 | m_position += valueLength; 105 | } 106 | } 107 | 108 | #if !defined(NDEBUG) 109 | assert(m_position <= m_size); 110 | #endif 111 | 112 | return value; 113 | } 114 | 115 | void seek(size_t position) const 116 | { 117 | m_position = position; 118 | } 119 | 120 | size_t getPos() const 121 | { 122 | return m_position; 123 | } 124 | 125 | bool isAtEnd() const 126 | { 127 | #if !defined(NDEBUG) 128 | assert(m_position <= m_size); 129 | #endif 130 | return m_position >= m_size; 131 | } 132 | }; 133 | 134 | #endif /* MMKVReader_h */ 135 | -------------------------------------------------------------------------------- /WechatExporter/core/PdfConverter.h: -------------------------------------------------------------------------------- 1 | // 2 | // PdfConverter.h 3 | // WechatExporter 4 | // 5 | // Created by Matthew on 2021/4/14. 6 | // Copyright © 2021 Matthew. All rights reserved. 7 | // 8 | 9 | #ifndef PdfConverter_h 10 | #define PdfConverter_h 11 | 12 | class PdfConverter 13 | { 14 | public: 15 | virtual bool makeUserDirectory(const std::string& dirName) = 0; 16 | virtual bool convert(const std::string& htmlPath, const std::string& pdfPath) = 0; 17 | virtual ~PdfConverter() {} 18 | }; 19 | 20 | #endif /* PdfConverter_h */ 21 | -------------------------------------------------------------------------------- /WechatExporter/core/ResManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // BaseResConverter.h 3 | // WechatExporter 4 | // 5 | // Created by Matthew on 2021/10/26. 6 | // Copyright © 2021 Matthew. All rights reserved. 7 | // 8 | 9 | #ifndef ResManager_h 10 | #define ResManager_h 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | #include "Template.h" 17 | 18 | class ResManager 19 | { 20 | public: 21 | 22 | struct EmojiItem 23 | { 24 | std::string m_tag; 25 | std::string m_fullTag; 26 | std::string m_title; 27 | std::string m_fileName; 28 | 29 | EmojiItem() 30 | { 31 | } 32 | 33 | EmojiItem(const std::string& tag, const std::string& fullTag, const std::string& title, const std::string& fileName) : m_tag(tag), m_fullTag(fullTag), m_title(title), m_fileName(fileName) 34 | { 35 | } 36 | 37 | inline bool operator<(const EmojiItem &rhs) const 38 | { 39 | return m_tag.compare(rhs.m_tag) < 0; 40 | } 41 | 42 | inline bool operator>(const EmojiItem &rhs) const 43 | { 44 | return m_tag.compare(rhs.m_tag) > 0; 45 | } 46 | 47 | const std::string& getTag() const 48 | { 49 | return m_tag; 50 | } 51 | 52 | const std::string& getTitle() const 53 | { 54 | return m_title; 55 | } 56 | 57 | const std::string& getFileName() const 58 | { 59 | return m_fileName; 60 | } 61 | 62 | bool equals(const std::string& tag) const 63 | { 64 | return m_tag.compare(tag) == 0; 65 | } 66 | }; 67 | 68 | struct EmojiItemCompare 69 | { 70 | inline bool operator() ( const EmojiItem& lhs, const std::string& rhs) const 71 | { 72 | return lhs.getTag().compare(rhs) < 0; 73 | } 74 | }; 75 | 76 | struct EmojiItemCompareN 77 | { 78 | inline bool operator() ( const EmojiItem& lhs, const std::string& rhs) const 79 | { 80 | return lhs.getTag().compare(rhs) < 0; 81 | } 82 | }; 83 | 84 | struct EmojiTag 85 | { 86 | std::string m_headTag; 87 | std::string m_tailTag; 88 | 89 | std::vector m_items; 90 | 91 | EmojiTag(const std::string& headTag, const std::string& tailTag) : m_headTag(headTag), m_tailTag(tailTag) 92 | { 93 | } 94 | 95 | bool hasTailTag() const 96 | { 97 | return !m_tailTag.empty(); 98 | } 99 | }; 100 | 101 | 102 | public: 103 | ResManager(); 104 | ~ResManager(); 105 | 106 | bool initLocaleResource(const std::string& resDir, const std::string& languageCode); 107 | bool initResources(const std::string& resDir, const std::string& languageCode, const std::string& templateName); 108 | 109 | // Default Resource 110 | std::string getDefaultAvatarPath() const; 111 | std::string getDefaultAppIconPath() const; 112 | 113 | // Localization 114 | std::string getLocaleString(const std::string& key) const; 115 | 116 | // Template 117 | std::string getTemplate(const std::string& key) const; 118 | // const Template& getNewTemplate(const std::string& key) const; 119 | std::string checkEmptyTemplates() const; 120 | 121 | const std::string& buildFromTemplate(const std::string& key, const std::map& values) const; 122 | 123 | // Emoji 124 | bool hasEmojiTag(const std::string& msg) const; 125 | std::string convertEmojis(const std::string& msg, const std::string& localRootPath, const std::string& emojiPath, const std::string& emojiUrlPath) const; 126 | 127 | static bool validateResources(const std::string& resDir, std::string& error); 128 | 129 | protected: 130 | bool loadLocaleStrings(const std::string& resDir, const std::string& languageCode); 131 | bool loadTemplates(const std::string& resDir, const std::string& templateName); 132 | bool loadEmojis(const std::string& resDir); 133 | 134 | protected: 135 | std::string m_resDir; 136 | std::map m_templates; 137 | std::map m_newTemplates; 138 | 139 | std::map m_localeStrings; 140 | std::vector m_emojiTags; 141 | 142 | std::string m_emptyString; 143 | }; 144 | 145 | #endif /* ResManager_h */ 146 | -------------------------------------------------------------------------------- /WechatExporter/core/TaskManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // SessionTaskManager.h 3 | // WechatExporter 4 | // 5 | // Created by Matthew on 2021/4/20. 6 | // Copyright © 2021 Matthew. All rights reserved. 7 | // 8 | 9 | #ifndef TaskManager_h 10 | #define TaskManager_h 11 | 12 | #include 13 | #include 14 | #include 15 | #include "WechatObjects.h" 16 | #include "AsyncExecutor.h" 17 | #include "PdfConverter.h" 18 | #include "Logger.h" 19 | 20 | class TaskManager : public AsyncExecutor::Callback 21 | { 22 | private: 23 | Logger* m_logger; 24 | 25 | AsyncExecutor *m_downloadExecutor; 26 | #ifdef USING_ASYNC_TASK_FOR_MP3 27 | AsyncExecutor *m_audioExecutor; 28 | #endif 29 | std::map m_downloadTasks; 30 | 31 | std::string m_userAgent; 32 | 33 | mutable std::mutex m_mutex; 34 | std::set m_downloadedFiles; 35 | std::map m_downloadingTasks; 36 | 37 | std::map> m_copyTaskQueue; 38 | 39 | #ifdef USING_ASYNC_TASK_FOR_MP3 40 | std::queue> m_Buffers; 41 | #endif 42 | 43 | public: 44 | 45 | TaskManager(Logger* logger); 46 | ~TaskManager(); 47 | 48 | virtual void onTaskStart(const AsyncExecutor* executor, const AsyncExecutor::Task *task); 49 | virtual void onTaskComplete(const AsyncExecutor* executor, const AsyncExecutor::Task *task, bool succeeded); 50 | 51 | void setUserAgent(const std::string& userAgent); 52 | 53 | size_t getNumberOfQueue(std::string& queueDesc) const; 54 | void cancel(); 55 | void shutdown(); 56 | // true: completed, false: timeout 57 | bool waitForCompltion(unsigned int ms); 58 | 59 | void download(const Session* session, const std::string &url, const std::string &backupUrl, const std::string& output, time_t mtime, const std::string& defaultFile = "", std::string type = ""); 60 | #ifdef USING_ASYNC_TASK_FOR_MP3 61 | enum AUDIO_FORMAT 62 | { 63 | AUDIO_FORMAT_AMR, 64 | AUDIO_FORMAT_SILK 65 | }; 66 | void convertAudio(const Session* session, const std::string& pcmPath, const std::string& mp3Path, AUDIO_FORMAT format, unsigned int mtime); 67 | #endif 68 | 69 | private: 70 | 71 | void shutdownExecutors(); 72 | 73 | inline std::set dequeueCopyTasks(uint32_t taskId) 74 | { 75 | std::set tasks; 76 | std::map>::iterator it = m_copyTaskQueue.find(taskId); 77 | if (it != m_copyTaskQueue.end()) 78 | { 79 | tasks.swap(it->second); 80 | m_copyTaskQueue.erase(it); 81 | } 82 | 83 | return tasks; 84 | } 85 | 86 | }; 87 | 88 | #endif /* SessionTaskManager_h */ 89 | -------------------------------------------------------------------------------- /WechatExporter/core/Template.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Template.cpp 3 | // WechatExporter 4 | // 5 | // Created by Matthew on 2022/4/21. 6 | // Copyright © 2022 Matthew. All rights reserved. 7 | // 8 | 9 | #include "Template.h" 10 | 11 | #define TEMPLATE_TAG "%%" 12 | #define TEMPLATE_TAG_LENGTH 2 13 | 14 | 15 | Template::Template() 16 | { 17 | } 18 | 19 | Template::Template(const std::string& tmpl) : m_template(tmpl) 20 | { 21 | size_t pos = 0; 22 | size_t posEnd = 0; 23 | while (1) 24 | { 25 | pos = m_template.find(TEMPLATE_TAG, pos); 26 | if (pos == std::string::npos) 27 | { 28 | break; 29 | } 30 | posEnd = m_template.find(TEMPLATE_TAG, pos + TEMPLATE_TAG_LENGTH); 31 | if (posEnd == std::string::npos) 32 | { 33 | break; 34 | } 35 | 36 | size_t length = posEnd - pos + TEMPLATE_TAG_LENGTH; 37 | std::string tag = m_template.substr(pos, length); 38 | 39 | m_tags.emplace_back(tag, pos, length); 40 | 41 | pos = posEnd + TEMPLATE_TAG_LENGTH; 42 | } 43 | } 44 | 45 | Template::Template(const Template& rhs) : m_template(rhs.m_template), m_tags(rhs.m_tags) 46 | { 47 | } 48 | 49 | Template& Template::operator=(const Template& rhs) 50 | { 51 | m_template = rhs.m_template; 52 | m_result = rhs.m_result; 53 | m_tags = rhs.m_tags; 54 | 55 | return *this; 56 | } 57 | 58 | const std::string& Template::build(const std::map& values) const 59 | { 60 | m_result.assign(m_template); 61 | 62 | for (auto it = m_tags.crbegin(); it != m_tags.crend(); ++it) 63 | { 64 | auto itVal = values.find(it->tag); 65 | if (itVal == values.cend()) 66 | { 67 | m_result.erase(m_result.begin() + it->pos, m_result.begin() + it->pos + it->length); 68 | } 69 | else 70 | { 71 | m_result.replace(it->pos, it->length, itVal->second); 72 | } 73 | } 74 | 75 | return m_result; 76 | } 77 | -------------------------------------------------------------------------------- /WechatExporter/core/Template.h: -------------------------------------------------------------------------------- 1 | // 2 | // Template.h 3 | // WechatExporter 4 | // 5 | // Created by Matthew on 2022/4/21. 6 | // Copyright © 2022 Matthew. All rights reserved. 7 | // 8 | 9 | #ifndef Template_h 10 | #define Template_h 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | struct TEMPLATE_TAG 17 | { 18 | std::string tag; 19 | size_t pos; 20 | size_t length; 21 | 22 | TEMPLATE_TAG(const std::string& t, size_t p, size_t l) : tag(t), pos(p), length(l) {} 23 | }; 24 | 25 | class Template 26 | { 27 | public: 28 | Template(); 29 | Template(const std::string& tmpl); 30 | 31 | Template(const Template& rhs); 32 | 33 | Template& operator=(const Template& rhs); 34 | 35 | std::string build(const std::vector>& values) const; 36 | 37 | const std::string& build(const std::map& values) const; 38 | 39 | protected: 40 | std::string m_template; 41 | mutable std::string m_result; 42 | std::vector m_tags; 43 | }; 44 | 45 | #endif /* Template_h */ 46 | -------------------------------------------------------------------------------- /WechatExporter/core/Updater.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Updater.cpp 3 | // WechatExporter 4 | // 5 | // Created by Matthew on 2021/3/6. 6 | // Copyright © 2021 Matthew. All rights reserved. 7 | // 8 | 9 | #include "Updater.h" 10 | #include "Downloader.h" 11 | #include "AsyncTask.h" 12 | #include "Utils.h" 13 | 14 | Updater::Updater(const std::string& currentVersion) : m_currentVersion(currentVersion) 15 | { 16 | } 17 | 18 | Updater::~Updater() 19 | { 20 | } 21 | 22 | void Updater::setUserAgent(const std::string& userAgent) 23 | { 24 | m_userAgent = userAgent; 25 | } 26 | 27 | std::string Updater::getNewVersion() const 28 | { 29 | return m_latestVersion; 30 | } 31 | 32 | std::string Updater::getUpdateUrl() const 33 | { 34 | return m_updateUrl; 35 | } 36 | 37 | bool Updater::checkUpdate() 38 | { 39 | m_latestVersion.clear(); 40 | m_updateUrl.clear(); 41 | 42 | std::vector body; 43 | std::vector> headers; 44 | if (!m_userAgent.empty()) 45 | { 46 | headers.push_back(std::pair("User-Agent", m_userAgent)); 47 | } 48 | 49 | std::string url = "https://src.wakin.org/github/wxexp/update.conf?v=" + encodeUrl(m_currentVersion); 50 | #ifndef NDEBUG 51 | headers.push_back(std::pair("RESOLVE", "src.wakin.org:443:127.0.0.1")); 52 | url += "&dbg=1"; 53 | #endif 54 | long httpStatus = 0; 55 | #ifdef USING_DOWNLOADER 56 | if (!Downloader::httpGet(url, headers, httpStatus, body) || httpStatus != 200 || body.empty()) 57 | #else 58 | if (!DownloadTask::httpGet(url, headers, httpStatus, body) || httpStatus != 200 || body.empty()) 59 | #endif 60 | { 61 | return false; 62 | } 63 | 64 | std::string bodyStr; 65 | bodyStr.assign(reinterpret_cast(&body[0]), body.size()); 66 | replaceAll(bodyStr, "\r\n", "\n"); 67 | replaceAll(bodyStr, "\r", "\n"); 68 | 69 | std::vector parts = split(bodyStr, "\n"); 70 | if (parts.empty() || parts[0].empty()) 71 | { 72 | return false; 73 | } 74 | 75 | std::vector versionParts = split(parts[0], "."); 76 | if (versionParts.size() != 4 || !isNumber(versionParts[0]) || !isNumber(versionParts[1]) || !isNumber(versionParts[2]) || !isNumber(versionParts[3])) 77 | { 78 | return false; 79 | } 80 | m_latestVersion = parts[0]; 81 | 82 | std::vector curVersionParts = split(m_currentVersion, "."); 83 | if (curVersionParts.size() != 4 || !isNumber(curVersionParts[0]) || !isNumber(curVersionParts[1]) || !isNumber(curVersionParts[2]) || !isNumber(curVersionParts[3])) 84 | { 85 | return false; 86 | } 87 | 88 | if (parts.size() > 1 && !parts[1].empty()) 89 | { 90 | m_updateUrl = parts[1]; 91 | } 92 | 93 | for (int idx = 0; idx < 4; ++idx) 94 | { 95 | int v = std::atoi(versionParts[idx].c_str()); 96 | int cv = std::atoi(curVersionParts[idx].c_str()); 97 | if (v > cv) 98 | { 99 | return true; 100 | } 101 | } 102 | 103 | return false; 104 | } 105 | -------------------------------------------------------------------------------- /WechatExporter/core/Updater.h: -------------------------------------------------------------------------------- 1 | // 2 | // Updater.h 3 | // WechatExporter 4 | // 5 | // Created by Matthew on 2021/3/6. 6 | // Copyright © 2021 Matthew. All rights reserved. 7 | // 8 | 9 | #ifndef Updater_h 10 | #define Updater_h 11 | 12 | #include 13 | 14 | class Updater 15 | { 16 | public: 17 | Updater(const std::string& currentVersion); 18 | ~Updater(); 19 | 20 | void setUserAgent(const std::string& userAgent); 21 | 22 | bool checkUpdate(); 23 | std::string getNewVersion() const; 24 | std::string getUpdateUrl() const; 25 | 26 | private: 27 | std::string m_currentVersion; 28 | std::string m_latestVersion; 29 | std::string m_updateUrl; 30 | std::string m_userAgent; 31 | }; 32 | 33 | #endif /* Updater_h */ 34 | -------------------------------------------------------------------------------- /WechatExporter/core/Utils_md5.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Utils.cpp 3 | // WechatExporter 4 | // 5 | // Created by Matthew on 2020/9/30. 6 | // Copyright © 2020 Matthew. All rights reserved. 7 | // 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | #if defined(_WIN32) 14 | #include 15 | #include 16 | 17 | #define MD5_DIGEST_LENGTH 16 18 | #define SHA_DIGEST_LENGTH 20 19 | 20 | #elif defined(__APPLE__) 21 | #import 22 | #else 23 | 24 | #endif 25 | 26 | #include "FileSystem.h" 27 | 28 | std::string md5Impl(const void* data, size_t dataSize) 29 | { 30 | std::stringstream stream; 31 | stream << std::setfill ('0') << std::hex; 32 | 33 | #if defined(_WIN32) 34 | 35 | HCRYPTPROV hCryptProv = NULL; 36 | HCRYPTHASH hHash = NULL; 37 | BYTE bHash[0x7f] = {0}; 38 | DWORD dwHashLen= MD5_DIGEST_LENGTH; // The MD5 algorithm always returns 16 bytes. 39 | 40 | if(CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_MACHINE_KEYSET)) 41 | { 42 | if(CryptCreateHash(hCryptProv, CALG_MD5, 0, 0, &hHash)) 43 | { 44 | if(CryptHashData(hHash, reinterpret_cast(data), static_cast(dataSize), 0)) 45 | { 46 | if(CryptGetHashParam(hHash, HP_HASHVAL, bHash, &dwHashLen, 0)) 47 | { 48 | // Make a string version of the numeric digest value 49 | for (int idx = 0; idx < 16; idx++) 50 | { 51 | stream << std::setw(2) << ((unsigned int) bHash[idx]); 52 | } 53 | } 54 | } 55 | } 56 | } 57 | 58 | CryptDestroyHash(hHash); 59 | CryptReleaseContext(hCryptProv, 0); 60 | 61 | #elif defined(__APPLE__) 62 | unsigned char digest[CC_MD5_DIGEST_LENGTH] = {0}; 63 | CC_MD5(data, (CC_LONG)dataSize, digest); // This is the md5 call 64 | 65 | for (int idx = 0; idx < CC_MD5_DIGEST_LENGTH; idx++) 66 | { 67 | stream << std::setw(2) << ((unsigned int) digest[idx]); 68 | } 69 | #else 70 | #error "Md5 Not implemented." 71 | #endif 72 | 73 | return stream.str(); 74 | } 75 | 76 | std::string md5(const std::string& s) 77 | { 78 | return md5Impl(s.c_str(), s.size()); 79 | } 80 | 81 | std::string md5File(const std::string& path) 82 | { 83 | std::vector data; 84 | 85 | if (readFile(path, data) && !data.empty()) 86 | { 87 | return md5Impl(&data[0], data.size()); 88 | } 89 | 90 | return ""; 91 | } 92 | 93 | std::string sha1(const std::string& s) 94 | { 95 | std::stringstream stream; 96 | stream << std::setfill ('0') << std::hex; 97 | 98 | #if defined(_WIN32) 99 | 100 | HCRYPTPROV hCryptProv = NULL; 101 | HCRYPTHASH hHash = NULL; 102 | BYTE bHash[0x7f] = {0}; 103 | DWORD dwHashLen= SHA_DIGEST_LENGTH ; // The SHA1 algorithm always returns 20 bytes. 104 | 105 | if(CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_MACHINE_KEYSET)) 106 | { 107 | if(CryptCreateHash(hCryptProv, CALG_SHA1, 0, 0, &hHash)) 108 | { 109 | if(CryptHashData(hHash, reinterpret_cast(s.c_str()), static_cast(s.size()), 0)) 110 | { 111 | if(CryptGetHashParam(hHash, HP_HASHVAL, bHash, &dwHashLen, 0)) 112 | { 113 | // Make a string version of the numeric digest value 114 | for (int idx = 0; idx < dwHashLen; idx++) 115 | { 116 | stream << std::setw(2) << ((unsigned int) bHash[idx]); 117 | } 118 | } 119 | } 120 | } 121 | } 122 | 123 | CryptDestroyHash(hHash); 124 | CryptReleaseContext(hCryptProv, 0); 125 | 126 | #elif defined(__APPLE__) 127 | unsigned char digest[CC_SHA1_DIGEST_LENGTH] = {0}; 128 | CC_SHA1(s.c_str(), (CC_LONG)s.size(), digest); // This is the md5 call 129 | 130 | for (int idx = 0; idx < CC_SHA1_DIGEST_LENGTH; idx++) 131 | { 132 | stream << std::setw(2) << ((unsigned int) digest[idx]); 133 | } 134 | #else 135 | #error "SHA1 Not implemented." 136 | #endif 137 | 138 | return stream.str(); 139 | } 140 | -------------------------------------------------------------------------------- /WechatExporter/core/Utils_thread.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Utils_thread.cpp 3 | // WechatExporter 4 | // 5 | // Created by Matthew on 2020/9/30. 6 | // Copyright © 2020 Matthew. All rights reserved. 7 | // 8 | #if 1 9 | #include "Utils.h" 10 | 11 | #ifdef _WIN32 12 | 13 | // https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2015/debugger/how-to-set-a-thread-name-in-native-code?view=vs-2015&redirectedfrom=MSDN 14 | // https://stackoverflow.com/questions/10121560/stdthread-naming-your-thread 15 | 16 | #include 17 | const DWORD MS_VC_EXCEPTION = 0x406D1388; 18 | 19 | #pragma pack(push, 8) 20 | typedef struct tagTHREADNAME_INFO 21 | { 22 | DWORD dwType; // Must be 0x1000. 23 | LPCSTR szName; // Pointer to name (in user addr space). 24 | DWORD dwThreadID; // Thread ID (-1=caller thread). 25 | DWORD dwFlags; // Reserved for future use, must be zero. 26 | } THREADNAME_INFO; 27 | #pragma pack(pop) 28 | 29 | void setThreadName(uint32_t dwThreadID, const char* threadName) 30 | { 31 | THREADNAME_INFO info; 32 | info.dwType = 0x1000; 33 | info.szName = threadName; 34 | info.dwThreadID = dwThreadID; 35 | info.dwFlags = 0; 36 | 37 | __try 38 | { 39 | RaiseException(MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info ); 40 | } 41 | __except(EXCEPTION_EXECUTE_HANDLER) 42 | { 43 | } 44 | } 45 | 46 | void setThreadName( const char* threadName) 47 | { 48 | setThreadName(GetCurrentThreadId(), threadName); 49 | } 50 | 51 | void setThreadName(std::thread* thread, const char* threadName) 52 | { 53 | DWORD threadId = ::GetThreadId(static_cast(thread->native_handle() ) ); 54 | setThreadName(threadId, threadName); 55 | } 56 | 57 | #elif defined(__linux__) 58 | #include 59 | void setThreadName(const char* threadName) 60 | { 61 | prctl(PR_SET_NAME, threadName, 0, 0, 0); 62 | } 63 | 64 | #else 65 | #include 66 | void setThreadName(const char* threadName) 67 | { 68 | pthread_setname_np(threadName); 69 | } 70 | #endif 71 | 72 | #endif // 0 73 | -------------------------------------------------------------------------------- /WechatExporter/core/Utils_xml.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Utils.cpp 3 | // WechatExporter 4 | // 5 | // Created by Matthew on 2020/9/30. 6 | // Copyright © 2020 Matthew. All rights reserved. 7 | // 8 | 9 | #include 10 | #include "Utils.h" 11 | #include 12 | #include 13 | #include 14 | 15 | bool getXmlNodeValue(const std::string& xml, const std::string& xpath, std::string& value) 16 | { 17 | bool result = false; 18 | value.clear(); 19 | 20 | xmlDocPtr doc = NULL; 21 | xmlXPathContextPtr xpathCtx = NULL; 22 | xmlXPathObjectPtr xpathObj = NULL; 23 | xmlNodeSetPtr xpathNodes = NULL; 24 | 25 | doc = xmlParseMemory(xml.c_str(), static_cast(xml.size())); 26 | if (doc == NULL) { goto end; } 27 | 28 | xpathCtx = xmlXPathNewContext(doc); 29 | if (xpathCtx == NULL) { goto end; } 30 | 31 | xpathObj = xmlXPathEvalExpression(BAD_CAST(xpath.c_str()), xpathCtx); 32 | if (xpathObj == NULL) { goto end; } 33 | 34 | xpathNodes = xpathObj->nodesetval; 35 | if ((xpathNodes) && (xpathNodes->nodeNr > 0)) 36 | { 37 | xmlNode *cur = xpathNodes->nodeTab[0]; 38 | xmlChar* sz = xmlNodeGetContent(cur); 39 | if (sz != NULL) 40 | { 41 | value = reinterpret_cast(sz);; 42 | xmlFree(sz); 43 | } 44 | result = true; 45 | } 46 | 47 | end: 48 | if (xpathObj) { xmlXPathFreeObject(xpathObj); } 49 | if (xpathCtx) { xmlXPathFreeContext(xpathCtx); } 50 | if (doc) { xmlFreeDoc(doc); } 51 | 52 | return result; 53 | } 54 | 55 | bool getXmlNodeAttributeValue(const std::string& xml, const std::string& xpath, const std::string& attributeName, std::string& value) 56 | { 57 | bool result = false; 58 | value.clear(); 59 | 60 | xmlDocPtr doc = NULL; 61 | xmlXPathContextPtr xpathCtx = NULL; 62 | xmlXPathObjectPtr xpathObj = NULL; 63 | xmlNodeSetPtr xpathNodes = NULL; 64 | 65 | doc = xmlParseMemory(xml.c_str(), static_cast(xml.size())); 66 | if (doc == NULL) { goto end; } 67 | 68 | xpathCtx = xmlXPathNewContext(doc); 69 | if (xpathCtx == NULL) { goto end; } 70 | 71 | xpathObj = xmlXPathEvalExpression(BAD_CAST(xpath.c_str()), xpathCtx); 72 | if (xpathObj == NULL) { goto end; } 73 | 74 | xpathNodes = xpathObj->nodesetval; 75 | if ((xpathNodes) && (xpathNodes->nodeNr > 0)) 76 | { 77 | xmlNode *cur = xpathNodes->nodeTab[0]; 78 | xmlChar* attr = xmlGetProp(cur, reinterpret_cast(attributeName.c_str())); 79 | if (NULL != attr) 80 | { 81 | value = reinterpret_cast(attr); 82 | xmlFree(attr); 83 | } 84 | 85 | result = true; 86 | } 87 | 88 | end: 89 | if (xpathObj) { xmlXPathFreeObject(xpathObj); } 90 | if (xpathCtx) { xmlXPathFreeContext(xpathCtx); } 91 | if (doc) { xmlFreeDoc(doc); } 92 | 93 | return result; 94 | } -------------------------------------------------------------------------------- /WechatExporter/core/WechatSource.h: -------------------------------------------------------------------------------- 1 | // 2 | // WechatDataSource.h 3 | // WechatExporter 4 | // 5 | // Created by Matthew on 2021/12/23. 6 | // Copyright © 2021 Matthew. All rights reserved. 7 | // 8 | 9 | #ifndef WechatDataSource_h 10 | #define WechatDataSource_h 11 | 12 | #include "ITunesParser.h" 13 | #include "IDeviceBackup.h" 14 | 15 | class WechatSource 16 | { 17 | public: 18 | WechatSource() : m_deviceInfo(NULL) 19 | { 20 | } 21 | WechatSource(const BackupItem& backupItem) : m_backupItem(backupItem), m_deviceInfo(NULL) 22 | { 23 | } 24 | WechatSource(const DeviceInfo& deviceInfo) : m_deviceInfo(NULL) 25 | { 26 | m_deviceInfo = new DeviceInfo(deviceInfo); 27 | } 28 | 29 | ~WechatSource() 30 | { 31 | if (NULL != m_deviceInfo) 32 | { 33 | delete m_deviceInfo; 34 | } 35 | } 36 | 37 | bool operator==(const WechatSource& rhs) 38 | { 39 | if (isDevice() != rhs.isDevice()) 40 | { 41 | return false; 42 | } 43 | 44 | if (isDevice()) 45 | { 46 | return m_deviceInfo->getUdid() == rhs.m_deviceInfo->getUdid(); 47 | } 48 | else 49 | { 50 | return m_backupItem.getPath() == rhs.m_backupItem.getPath(); 51 | } 52 | 53 | } 54 | 55 | const BackupItem& getBackupItem() const 56 | { 57 | return m_backupItem; 58 | } 59 | 60 | void setBackupItem(const BackupItem& backupItem) 61 | { 62 | m_backupItem = backupItem; 63 | } 64 | 65 | const DeviceInfo* getDeviceInfo() const 66 | { 67 | return m_deviceInfo; 68 | } 69 | 70 | DeviceInfo* getDeviceInfo() 71 | { 72 | return m_deviceInfo; 73 | } 74 | 75 | bool isDevice() const 76 | { 77 | return NULL != m_deviceInfo; 78 | } 79 | 80 | std::string getDisplayName() const 81 | { 82 | if (isDevice()) 83 | { 84 | return m_deviceInfo->getName(); 85 | } 86 | 87 | return m_backupItem.toString(); 88 | } 89 | private: 90 | 91 | BackupItem m_backupItem; 92 | DeviceInfo* m_deviceInfo; 93 | }; 94 | 95 | #endif /* WechatDataSource_h */ 96 | -------------------------------------------------------------------------------- /WechatExporter/core/endianness.h: -------------------------------------------------------------------------------- 1 | #ifndef ENDIANNESS_H 2 | #define ENDIANNESS_H 3 | 4 | #ifndef __LITTLE_ENDIAN 5 | #define __LITTLE_ENDIAN 1234 6 | #endif 7 | 8 | #ifndef __BIG_ENDIAN 9 | #define __BIG_ENDIAN 4321 10 | #endif 11 | 12 | #ifndef __BYTE_ORDER 13 | #ifdef __LITTLE_ENDIAN__ 14 | #define __BYTE_ORDER __LITTLE_ENDIAN 15 | #else 16 | #ifdef __BIG_ENDIAN__ 17 | #define __BYTE_ORDER __BIG_ENDIAN 18 | #endif 19 | #endif 20 | #endif 21 | 22 | #ifndef be16toh 23 | #if __BYTE_ORDER == __BIG_ENDIAN 24 | #define be16toh(x) (x) 25 | #else 26 | #define be16toh(x) ((((x) & 0xFF00) >> 8) | (((x) & 0x00FF) << 8)) 27 | #endif 28 | #endif 29 | 30 | #ifndef htobe16 31 | #define htobe16 be16toh 32 | #endif 33 | 34 | #ifndef le16toh 35 | #if __BYTE_ORDER == __BIG_ENDIAN 36 | #define le16toh(x) ((((x) & 0xFF00) >> 8) | (((x) & 0x00FF) << 8)) 37 | #else 38 | #define le16toh(x) (x) 39 | #endif 40 | #endif 41 | 42 | #ifndef htole16 43 | #define htole16 le16toh 44 | #endif 45 | 46 | #ifndef __bswap_32 47 | #define __bswap_32(x) ((((x) & 0xFF000000) >> 24) \ 48 | | (((x) & 0x00FF0000) >> 8) \ 49 | | (((x) & 0x0000FF00) << 8) \ 50 | | (((x) & 0x000000FF) << 24)) 51 | #endif 52 | 53 | #ifndef be32toh 54 | #if __BYTE_ORDER == __BIG_ENDIAN 55 | #define be32toh(x) (x) 56 | #else 57 | #define be32toh(x) __bswap_32(x) 58 | #endif 59 | #endif 60 | 61 | #ifndef htobe32 62 | #define htobe32 be32toh 63 | #endif 64 | 65 | #ifndef le32toh 66 | #if __BYTE_ORDER == __BIG_ENDIAN 67 | #define le32toh(x) __bswap_32(x) 68 | #else 69 | #define le32toh(x) (x) 70 | #endif 71 | #endif 72 | 73 | #ifndef htole32 74 | #define htole32 le32toh 75 | #endif 76 | 77 | #ifndef __bswap_64 78 | #define __bswap_64(x) ((((x) & 0xFF00000000000000ull) >> 56) \ 79 | | (((x) & 0x00FF000000000000ull) >> 40) \ 80 | | (((x) & 0x0000FF0000000000ull) >> 24) \ 81 | | (((x) & 0x000000FF00000000ull) >> 8) \ 82 | | (((x) & 0x00000000FF000000ull) << 8) \ 83 | | (((x) & 0x0000000000FF0000ull) << 24) \ 84 | | (((x) & 0x000000000000FF00ull) << 40) \ 85 | | (((x) & 0x00000000000000FFull) << 56)) 86 | #endif 87 | 88 | #ifndef htobe64 89 | #if __BYTE_ORDER == __BIG_ENDIAN 90 | #define htobe64(x) (x) 91 | #else 92 | #define htobe64(x) __bswap_64(x) 93 | #endif 94 | #endif 95 | 96 | #ifndef be64toh 97 | #define be64toh htobe64 98 | #endif 99 | 100 | #ifndef le64toh 101 | #if __BYTE_ORDER == __LITTLE_ENDIAN 102 | #define le64toh(x) (x) 103 | #else 104 | #define le64toh(x) __bswap_64(x) 105 | #endif 106 | #endif 107 | 108 | #ifndef htole64 109 | #define htole64 le64toh 110 | #endif 111 | 112 | #endif /* ENDIANNESS_H */ 113 | -------------------------------------------------------------------------------- /WechatExporter/core/md5.h: -------------------------------------------------------------------------------- 1 | /* See md5.c for explanation and copyright information. */ 2 | // https://opensource.apple.com/source/cvs/cvs-19/cvs/lib/md5.h 3 | // https://opensource.apple.com/source/cvs/cvs-19/cvs/lib/md5.c 4 | 5 | #ifndef MD5_H 6 | #define MD5_H 7 | 8 | /* Unlike previous versions of this code, uint32 need not be exactly 9 | 32 bits, merely 32 bits or more. Choosing a data type which is 32 10 | bits instead of 64 is not important; speed is considerably more 11 | important. ANSI guarantees that "unsigned long" will be big enough, 12 | and always using it seems to have few disadvantages. */ 13 | typedef unsigned long uint32; 14 | 15 | struct MD5Context { 16 | uint32 buf[4]; 17 | uint32 bits[2]; 18 | unsigned char in[64]; 19 | }; 20 | 21 | /* 22 | void MD5Init PROTO((struct MD5Context *context)); 23 | void MD5Update PROTO((struct MD5Context *context, unsigned char const *buf, unsigned len)); 24 | void MD5Final PROTO((unsigned char digest[16], struct MD5Context *context)); 25 | void MD5Transform PROTO((uint32 buf[4], const unsigned char in[64])); 26 | */ 27 | void MD5Init (struct MD5Context *context); 28 | void MD5Update (struct MD5Context *context, unsigned char const *buf, unsigned len); 29 | void MD5Final (unsigned char digest[16], struct MD5Context *context); 30 | void MD5Transform (uint32 buf[4], const unsigned char in[64]); 31 | 32 | /* 33 | * This is needed to make RSAREF happy on some MS-DOS compilers. 34 | */ 35 | typedef struct MD5Context MD5_CTX; 36 | 37 | #endif /* !MD5_H */ 38 | -------------------------------------------------------------------------------- /WechatExporter/core/semaphore.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | class semaphore 7 | { 8 | private: 9 | std::mutex mutex_; 10 | std::condition_variable condition_; 11 | unsigned long count_ = 0; // Initialized as locked. 12 | 13 | public: 14 | void notify() { 15 | std::lock_guard lock(mutex_); 16 | ++count_; 17 | condition_.notify_one(); 18 | } 19 | 20 | void wait() { 21 | std::unique_lock lock(mutex_); 22 | while (!count_) // Handle spurious wake-ups. 23 | condition_.wait(lock); 24 | --count_; 25 | } 26 | 27 | bool try_wait() { 28 | std::lock_guard lock(mutex_); 29 | if (count_) { 30 | --count_; 31 | return true; 32 | } 33 | return false; 34 | } 35 | }; 36 | 37 | 38 | -------------------------------------------------------------------------------- /WechatExporter/en.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | /* 2 | Localizable.strings 3 | WechatExporter 4 | 5 | Created by Matthew on 2021/3/10. 6 | Copyright © 2021 Matthew. All rights reserved. 7 | */ 8 | 9 | "btn-yes" = "Yes"; 10 | "btn-no" = "NO"; 11 | "btn-ok" = "OK"; 12 | "btn-cancel" = "Cancel"; 13 | "err-no-output-dir" = "Please choose a output directory."; 14 | "err-output-dir-doesnt-exist" = "Output directory doesn't exist."; 15 | "err-backup-dir-doesnt-exist" = "iTunes backup directory doesn't exist."; 16 | "err-no-backup-dir" = "Please choose an iTunes backup directory."; 17 | "err-encrypted-bkp-not-supported" = "Encrypted iTunes Backup is not supported."; 18 | "err-exp-is-running" = "Export is running."; 19 | "err-failed-to-parse-backup" = "Failed to parse iTunes Backup file."; 20 | "err-wrong-param" = "Wrong parameters."; 21 | "txt-all-wechat-users" = "All WeChat Accounts"; 22 | "prompt-new-version-found" = "Found a new version: %@. Download it now?"; 23 | "session-deleted" = "(Deleted)"; 24 | "grant-full-disk-access" = "You should grant Full Disk Access to WechatExporter program as it reads data from iTunes Backup: Apple Menu › System Preferences › Security & Privacy › Privacy › Full Disk Access"; 25 | "btn-grant-full-disk-access" = "Grant Full Disk Access"; 26 | "not-text-msg" = "-"; 27 | "show-logs" = "Show Logs"; 28 | "hide-logs" = "Hide Logs"; 29 | "alert-update-options" = "You may change exporting format and options in main menu:"; 30 | "no-prev-exp-found" = "There is no previous exporting in output directory and [Incremental Exporting] will be ignored."; 31 | "prev-exp-found" = "The previous exporting at %@ found, will reuse previous options."; 32 | "invld-inc-exp-for-multi-users" = "Incremental Exporting will be invalid for multiple WeChat accounts as for wrong design. \rPlease choose another directory for exporting."; 33 | -------------------------------------------------------------------------------- /WechatExporter/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // WechatExporter 4 | // 5 | // Created by Matthew on 2020/9/29. 6 | // Copyright © 2020 Matthew. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | int main(int argc, const char * argv[]) { 13 | @autoreleasepool { 14 | // Setup code that might create autoreleased objects goes here. 15 | } 16 | 17 | return NSApplicationMain(argc, argv); 18 | } 19 | -------------------------------------------------------------------------------- /WechatExporter/res/DefaultAppIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/DefaultAppIcon.png -------------------------------------------------------------------------------- /WechatExporter/res/DefaultAvatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/DefaultAvatar.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Aaagh!.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Aaagh!.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Angry.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Angry.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Awesome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Awesome.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Awkward.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Awkward.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Bah!L.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Bah!L.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Bah!R.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Bah!R.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Basketball.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Basketball.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Beckon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Beckon.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Beer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Beer.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Blessing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Blessing.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Blowkiss.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Blowkiss.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Blush.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Blush.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Bomb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Bomb.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Boring.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Boring.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Broken.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Broken.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/BrokenHeart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/BrokenHeart.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Bye.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Bye.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Cake.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Cake.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Candle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Candle.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Chick.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Chick.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Chuckle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Chuckle.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Clap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Clap.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Cleaver.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Cleaver.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Coffee.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Coffee.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Commando.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Commando.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Concerned.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Concerned.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/CoolGuy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/CoolGuy.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Cry.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Cry.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Dagger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Dagger.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Determined.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Determined.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Dizzy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Dizzy.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Doge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Doge.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Dramatic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Dramatic.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Drool.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Drool.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Drowsy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Drowsy.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Duh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Duh.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Emm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Emm.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Facepalm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Facepalm.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Fight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Fight.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Firecracker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Firecracker.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Fireworks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Fireworks.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Fist.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Fist.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Flushed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Flushed.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Frown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Frown.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Gift.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Gift.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/GoForIt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/GoForIt.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Grimace.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Grimace.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Grin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Grin.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Hammer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Hammer.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Happy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Happy.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Heart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Heart.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Hey.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Hey.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Hooray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Hooray.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Hug.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Hug.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Hungry.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Hungry.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Hurt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Hurt.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/InLove.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/InLove.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Joyful.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Joyful.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/JumpRope.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/JumpRope.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/KeepFighting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/KeepFighting.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Kiss.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Kiss.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Kotow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Kotow.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Ladybug.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Ladybug.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Laugh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Laugh.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Let Down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Let Down.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/LetMeSee.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/LetMeSee.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Lightning.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Lightning.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Lips.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Lips.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Lol.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Lol.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Meditate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Meditate.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Moon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Moon.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Moue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Moue.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/MyBad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/MyBad.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/NoProb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/NoProb.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/NosePick.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/NosePick.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Nuh-uh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Nuh-uh.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/OK.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/OK.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/OMG.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/OMG.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Onlooker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Onlooker.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Packet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Packet.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Panic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Panic.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Party.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Party.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Peace.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Peace.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Pig.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Pig.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/PingPong.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/PingPong.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Pinky.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Pinky.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Pooh-pooh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Pooh-pooh.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Poop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Poop.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Puke.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Puke.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Pup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Pup.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Respect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Respect.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Rice.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Rice.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Rich.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Rich.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/RockOn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/RockOn.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Rose.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Rose.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Ruthless.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Ruthless.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Scold.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Scold.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Scowl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Scowl.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Scream.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Scream.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Shake.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Shake.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Shame.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Shame.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Shhh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Shhh.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Shocked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Shocked.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Shrunken.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Shrunken.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Shy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Shy.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Sick.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Sick.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Sigh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Sigh.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Silent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Silent.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Skull.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Skull.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Sleep.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Sleep.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Slight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Slight.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Sly.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Sly.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Smart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Smart.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Smile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Smile.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Smirk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Smirk.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Smooch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Smooch.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Smug.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Smug.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Sob.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Sob.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Soccer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Soccer.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Speechless.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Speechless.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Sun.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Sun.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Surprise.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Surprise.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Surrender.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Surrender.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Sweat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Sweat.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Sweats.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Sweats.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/TaiChi L.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/TaiChi L.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/TaiChi R.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/TaiChi R.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Tea.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Tea.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/TearingUp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/TearingUp.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Terror.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Terror.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/ThumbsDown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/ThumbsDown.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/ThumbsUp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/ThumbsUp.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Toasted.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Toasted.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Tongue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Tongue.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Tormented.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Tormented.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Tremble.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Tremble.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Trick.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Trick.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Twirl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Twirl.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Waddle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Waddle.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Watermelon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Watermelon.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Wave.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Wave.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Whimper.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Whimper.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Wilt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Wilt.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Worship.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Worship.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Wow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Wow.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Wrath.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Wrath.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Yawn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Yawn.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/Yeah!.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/Yeah!.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/u1F47B.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/u1F47B.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/u1F4AA.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/u1F4AA.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/u1F604.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/u1F604.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/u1F61D.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/u1F61D.png -------------------------------------------------------------------------------- /WechatExporter/res/emoji/images/u1F633.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/emoji/images/u1F633.png -------------------------------------------------------------------------------- /WechatExporter/res/en.txt: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "key": "Transfer_Subtype_1", 4 | "value": "Transfer" 5 | }, 6 | 7 | { 8 | "key": "Transfer_Subtype_3", 9 | "value": "Accepted " 10 | }, 11 | { 12 | "key": "Transfer_Subtype_5", 13 | "value": "Accepted " 14 | }, 15 | { 16 | "key": "Transfer_Subtype_4", 17 | "value": "Rejected" 18 | }, 19 | 20 | { 21 | "key": "Transfer_Subtype_8", 22 | "value": "Accepted" 23 | }, 24 | { 25 | "key": "Transfer_Subtype_9", 26 | "value": "Rejected" 27 | }, 28 | { 29 | "key": "Transfer_Subtype_10", 30 | "value": "Expired" 31 | }, 32 | 33 | { 34 | "key": "Transfer_Subtype_1", 35 | "value": "Pay to %s" 36 | }, 37 | 38 | { 39 | "key": "Group_Transfer_Subtype_3", 40 | "value": "From %s - Accepted " 41 | }, 42 | { 43 | "key": "Group_Transfer_Subtype_5", 44 | "value": "From %s - Accepted " 45 | }, 46 | { 47 | "key": "Group_Transfer_Subtype_4", 48 | "value": "From %s - Rejected" 49 | }, 50 | 51 | { 52 | "key": "Group_Transfer_Subtype_8", 53 | "value": "Pay to %s - Accepted" 54 | }, 55 | { 56 | "key": "Group_Transfer_Subtype_9", 57 | "value": "Pay to %s - Rejected" 58 | }, 59 | { 60 | "key": "Group_Transfer_Subtype_10", 61 | "value": "Expired" 62 | }, 63 | { 64 | "key": "", 65 | "value": "" 66 | }, 67 | { 68 | "key": "", 69 | "value": "" 70 | } 71 | ] 72 | -------------------------------------------------------------------------------- /WechatExporter/res/iPhone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/iPhone.png -------------------------------------------------------------------------------- /WechatExporter/res/iTunes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/iTunes.png -------------------------------------------------------------------------------- /WechatExporter/res/templates/audio.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 |
5 |
%%NAME%% %%TIME%%
6 |
7 |
8 |
9 |
%%AUDIOTIME%%
10 |
11 |
12 |
13 | -------------------------------------------------------------------------------- /WechatExporter/res/templates/card.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 |
5 |
%%NAME%% %%TIME%%
6 |
7 |
8 | %%CARDNAME%% 9 |
10 |
11 | %%CARDTYPE%% 12 |
13 |
14 |
15 | -------------------------------------------------------------------------------- /WechatExporter/res/templates/channels.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 |
5 |
%%NAME%% %%TIME%%
6 |
7 |
8 |
9 |
10 | %%CARDNAME%% 11 |
12 |
13 |
%%MESSAGE%%
14 |
15 |
16 | %%CHANNELS%% 17 |
18 |
19 |
20 | -------------------------------------------------------------------------------- /WechatExporter/res/templates/emoji.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 |
5 |
%%NAME%% %%TIME%%
6 |
7 | %%EMOJI_TITLE%% 8 |
9 |
10 | -------------------------------------------------------------------------------- /WechatExporter/res/templates/filter.html: -------------------------------------------------------------------------------- 1 | 2 |   3 | %%ML:Photos%% 4 | %%ML:Videos%% 5 | %%ML:All%% 6 | -------------------------------------------------------------------------------- /WechatExporter/res/templates/image.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 |
5 |
%%NAME%% %%TIME%%
6 |
7 | 8 |
9 |
10 | -------------------------------------------------------------------------------- /WechatExporter/res/templates/listframe.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 20 | %%ML:WeChat Chat History%% %%USERNAME%% 21 | 22 | 23 | 24 | %%TBODY%% 25 |
26 | 27 | 28 | -------------------------------------------------------------------------------- /WechatExporter/res/templates/listitem.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | %%ITEMTEXT%% 7 | 8 | 9 | -------------------------------------------------------------------------------- /WechatExporter/res/templates/member.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 |
5 |
%%NAME%% %%TIME%%
6 |
7 | 8 |
9 |
10 | -------------------------------------------------------------------------------- /WechatExporter/res/templates/members.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 |
5 |
%%NAME%% %%TIME%%
6 |
7 | 8 |
9 |
10 | -------------------------------------------------------------------------------- /WechatExporter/res/templates/msg.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 |
5 |
%%NAME%% %%TIME%%
6 |
7 |
8 |
%%MESSAGE%%
9 |
10 |
11 | -------------------------------------------------------------------------------- /WechatExporter/res/templates/notice.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
%%MESSAGE%%
4 |
5 |
6 | -------------------------------------------------------------------------------- /WechatExporter/res/templates/plainshare.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 |
5 |
%%NAME%% %%TIME%%
6 |
7 |
8 |
9 |
10 |
%%SHARINGTITLE%%
11 |
12 |
13 |
%%MESSAGE%%
14 |
15 |
16 |
17 | %%APPNAME%% 18 |
19 | 20 |
21 |
22 | -------------------------------------------------------------------------------- /WechatExporter/res/templates/refermsg.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 |
5 |
%%NAME%% %%TIME%%
6 |
7 |
8 |
「%%REFERNAME%%:%%REFERMSG%%」

- - - - - - - - - - - - - - -
%%MESSAGE%%
9 |
10 |
11 | -------------------------------------------------------------------------------- /WechatExporter/res/templates/scripts.html: -------------------------------------------------------------------------------- 1 | (function() { 2 | var msgArray = %%JSON_DATA%%; 3 | for (var idx = 0; idx < msgArray.length; idx++) 4 | { 5 | window.moreWechatMsgs.push(msgArray[idx]); 6 | } 7 | 8 | msgArray = null; 9 | loadMsgsForNextPage(); 10 | })(); 11 | 12 | 13 | -------------------------------------------------------------------------------- /WechatExporter/res/templates/share.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 |
5 |
%%NAME%% %%TIME%%
6 |
7 |
8 |
9 |
10 | 11 |
12 |
13 |
14 | %%SHARINGTITLE%% 15 |
16 |
17 |
%%MESSAGE%%
18 |
19 |
20 |
21 |
22 | %%APPNAME%% 23 |
24 |
25 |
26 | -------------------------------------------------------------------------------- /WechatExporter/res/templates/system.html: -------------------------------------------------------------------------------- 1 |
%%ML:System Message:%% %%MESSAGE%%
2 | -------------------------------------------------------------------------------- /WechatExporter/res/templates/thumb.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 |
5 |
%%NAME%% %%TIME%%
6 |
7 | %%MESSAGE%% 8 |
9 |
10 | -------------------------------------------------------------------------------- /WechatExporter/res/templates/transfer.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 |
5 |
%%NAME%% %%TIME%%
6 |
7 |
8 |
9 |
10 |
%%MESSAGE%%
11 |
12 |
13 |
14 | %%ML:Weixin Transfer%% 15 |
16 | 17 |
18 |
19 | -------------------------------------------------------------------------------- /WechatExporter/res/templates/video.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 |
5 |
%%NAME%% %%TIME%%
6 |
7 | 8 |
9 |
10 | -------------------------------------------------------------------------------- /WechatExporter/res/templates/videonew.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 |
5 |
%%NAME%% %%TIME%%
6 |
7 | 8 |
9 |
10 | -------------------------------------------------------------------------------- /WechatExporter/res/templates/wxemoji.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /WechatExporter/res/templates_txt/audio.html: -------------------------------------------------------------------------------- 1 | %%NAME%% (%%TIME%%):%%MESSAGE%% 2 | -------------------------------------------------------------------------------- /WechatExporter/res/templates_txt/card.html: -------------------------------------------------------------------------------- 1 | %%NAME%% (%%TIME%%):%%CARDTYPE%% %%CARDNAME%% 2 | -------------------------------------------------------------------------------- /WechatExporter/res/templates_txt/channels.html: -------------------------------------------------------------------------------- 1 | %%NAME%% (%%TIME%%):%%MESSAGE%% - %%CHANNELS%% %%CARDNAME%% 2 | -------------------------------------------------------------------------------- /WechatExporter/res/templates_txt/emoji.html: -------------------------------------------------------------------------------- 1 | %%NAME%% (%%TIME%%):%%ML:[Emoji]%% 2 | -------------------------------------------------------------------------------- /WechatExporter/res/templates_txt/frame.html: -------------------------------------------------------------------------------- 1 | %%DISPLAYNAME%% - %%ML:WeChat Chat History%% 2 | 3 | %%BODY%% 4 | -------------------------------------------------------------------------------- /WechatExporter/res/templates_txt/image.html: -------------------------------------------------------------------------------- 1 | %%NAME%% (%%TIME%%):%%ML:[Photo]%% 2 | -------------------------------------------------------------------------------- /WechatExporter/res/templates_txt/listframe.html: -------------------------------------------------------------------------------- 1 | %%ML:WeChat Chat History%% %%USERNAME%% 2 | 3 | 4 | %%TBODY%% 5 | -------------------------------------------------------------------------------- /WechatExporter/res/templates_txt/listitem.html: -------------------------------------------------------------------------------- 1 | %%ITEMLINK%% 2 | 3 | -------------------------------------------------------------------------------- /WechatExporter/res/templates_txt/msg.html: -------------------------------------------------------------------------------- 1 | %%NAME%% (%%TIME%%):%%MESSAGE%% 2 | -------------------------------------------------------------------------------- /WechatExporter/res/templates_txt/notice.html: -------------------------------------------------------------------------------- 1 | %%MESSAGE%% 2 | -------------------------------------------------------------------------------- /WechatExporter/res/templates_txt/plainshare.html: -------------------------------------------------------------------------------- 1 | %%NAME%% (%%TIME%%):%%SHARINGTITLE%% 2 | -------------------------------------------------------------------------------- /WechatExporter/res/templates_txt/refermsg.html: -------------------------------------------------------------------------------- 1 | 「%%REFERNAME%%:%%REFERMSG%%」 2 | - - - - - - - - - - - - - - - 3 | %%MESSAGE%% 4 | -------------------------------------------------------------------------------- /WechatExporter/res/templates_txt/scripts.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/WechatExporter/res/templates_txt/scripts.html -------------------------------------------------------------------------------- /WechatExporter/res/templates_txt/share.html: -------------------------------------------------------------------------------- 1 | %%NAME%% (%%TIME%%):%%SHARINGTITLE%% - %%APPNAME%% 2 | -------------------------------------------------------------------------------- /WechatExporter/res/templates_txt/system.html: -------------------------------------------------------------------------------- 1 | %%ML:System Message:%% %%MESSAGE%% 2 | -------------------------------------------------------------------------------- /WechatExporter/res/templates_txt/thumb.html: -------------------------------------------------------------------------------- 1 | %%NAME%% (%%TIME%%):%%MESSAGE%% 2 | -------------------------------------------------------------------------------- /WechatExporter/res/templates_txt/transfer.html: -------------------------------------------------------------------------------- 1 | %%NAME%% (%%TIME%%):%%ML:Weixin Transfer%% %%MESSAGE%% -------------------------------------------------------------------------------- /WechatExporter/res/templates_txt/video.html: -------------------------------------------------------------------------------- 1 | %%NAME%% (%%TIME%%):%%ML:[Video]%% 2 | -------------------------------------------------------------------------------- /WechatExporter/res/templates_txt/wxemoji.html: -------------------------------------------------------------------------------- 1 | [%%EMOJI_RAW%%] -------------------------------------------------------------------------------- /WechatExporter/zh-Hans.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | /* 2 | Localizable.strings 3 | WechatExporter 4 | 5 | Created by Matthew on 2021/3/10. 6 | Copyright © 2021 Matthew. All rights reserved. 7 | */ 8 | 9 | "btn-yes" = "是"; 10 | "btn-no" = "否"; 11 | "btn-ok" = "确定"; 12 | "btn-cancel" = "取消"; 13 | "err-no-output-dir" = "请选择输出目录。"; 14 | "err-output-dir-doesnt-exist" = "输出目录不存在。"; 15 | "err-backup-dir-doesnt-exist" = "iTunes备份目录不存在。"; 16 | "err-no-backup-dir" = "请选择iTunes备份目录。"; 17 | "err-encrypted-bkp-not-supported" = "不支持加密的iTunes备份。"; 18 | "err-exp-is-running" = "导出已经在执行。"; 19 | "err-failed-to-parse-backup" = "解析iTunes Backup文件失败。"; 20 | "err-wrong-param" = "参数错误。"; 21 | "txt-all-wechat-users" = "所有微信账户"; 22 | "prompt-new-version-found" = "发现新版本:%@,是否下载?"; 23 | "session-deleted" = "(已删除)"; 24 | "grant-full-disk-access" = "因为WechatExporter程序需要读取iTunes备份数据,请先授权完全磁盘访问权限:苹果菜单 › 系统偏好设置... › 安全心与隐私 › 隐私 › 完全磁盘访问权限"; 25 | "btn-grant-full-disk-access" = "授权完全磁盘访问权限"; 26 | "not-text-msg" = "-"; 27 | "show-logs" = "显示日志"; 28 | "hide-logs" = "隐藏日志"; 29 | "alert-update-options" = "你可以在主菜单中修改导出格式和选项:"; 30 | "no-prev-exp-found" = "输出目录下不存在前一次导出的信息,【增量导出】将被忽略。"; 31 | "prev-exp-found" = "输出目录下发现了%s的导出数据,本次导出将采用“增量导出”模式,并复用前一次的设置。"; 32 | "invld-inc-exp-for-multi-users" = "由于设计错误,对于多个微信账号的增量备份复原可能产生错误,请更换新的目录导出。"; 33 | -------------------------------------------------------------------------------- /WechatExporterCmd.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /WechatExporterCmd.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /WechatExporterCmd.xcodeproj/xcshareddata/xcschemes/WechatExporterCmd.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 45 | 51 | 52 | 53 | 54 | 57 | 58 | 61 | 62 | 65 | 66 | 69 | 70 | 73 | 74 | 75 | 76 | 82 | 84 | 90 | 91 | 92 | 93 | 95 | 96 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /WechatExporterCmd/WechatExporterCmd.h: -------------------------------------------------------------------------------- 1 | // 2 | // main.cpp 3 | // WechatExporterCmd 4 | // 5 | // Created by Matthew on 2022/3/4. 6 | // 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | 14 | #ifdef _WIN32 15 | 16 | #elif defined(__APPLE__) 17 | #include "Utils.h" 18 | #include "Exporter.h" 19 | #include "IDeviceBackup.h" 20 | #include "WechatSource.h" 21 | #else 22 | #endif 23 | 24 | #ifndef WechatExporterCmd_h 25 | #define WechatExporterCmd_h 26 | 27 | #define OUTPUT_FORMAT_HTML 0 28 | #define OUTPUT_FORMAT_TEXT 1 29 | 30 | #define HTML_OPTION_SYNC 0 31 | #define HTML_OPTION_ONSCROLL 1 32 | #define HTML_OPTION_ONINIT 2 33 | 34 | class Logger; 35 | 36 | void printHelp(const char *executableName) 37 | { 38 | std::cout 39 | << 40 | "Usage: " << executableName 41 | << " [OPTION]\n" 42 | "Export Wechat chat history based on the options given:\n" 43 | " --backup=PATH Specify the directory of iTunes Backup\n" 44 | " --output=PATH Specify the directory in that Wechat chat history will be exported.\n" 45 | " --format=FORMAT FORMAT may be one of 'html', 'text'. 'html' is default.\n" 46 | " --account=ACCOUNT Specify the WeChat account which will be exported.\n" 47 | " --session=SESSION Friend name or chat group name which will be exported. May be specified multiple times\n" 48 | " If no session is specified, all sessions of the account will be exported.\n" 49 | " --asyncloading=[HTML LOADING OPTION]\n" 50 | " [HTML LOADING OPTION] may be one of 'sync', 'onscroll', 'oninit'. 'onscroll' is default.\n" 51 | " --filter=FILTER FILTER may be one of 'no', 'yes'. 'no' is default.\n" 52 | " --help Show this help.\n" 53 | << std::endl; 54 | } 55 | 56 | std::string parseArgumentwithQuato(const char *path) 57 | { 58 | std::string parsedPath; 59 | if (NULL == path) 60 | { 61 | return parsedPath; 62 | } 63 | 64 | size_t start = 0; 65 | size_t length = strlen(path); 66 | if (length > 1 && path[length - 1] == '"') 67 | { 68 | length--; 69 | } 70 | if (path[0] == '"') 71 | { 72 | start = 1; 73 | length--; 74 | } 75 | 76 | parsedPath = std::string(path, start, length); 77 | return parsedPath; 78 | } 79 | 80 | int exportSessions(const std::string& languageCode, Logger* logger, const std::string& workDir, const std::string& backupDir, const std::string& outputDir, const std::string& account, const std::vector& sessions, int outputFormat, int asyncLoading, bool outputFilter) 81 | { 82 | // const std::string& workDir, const std::string& backup, const std::string& output, Logger* logger, PdfConverter* pdfConverter 83 | 84 | Exporter exp(workDir, backupDir, outputDir, logger, NULL); 85 | exp.setLanguageCode(languageCode); 86 | ExportOption options; 87 | 88 | if (outputFormat == OUTPUT_FORMAT_TEXT) 89 | { 90 | options.setTextMode(); 91 | exp.setExtName("txt"); 92 | exp.setTemplatesName("templates_txt"); 93 | } 94 | else 95 | { 96 | exp.setExtName("html"); 97 | exp.setTemplatesName("templates"); 98 | if (asyncLoading == HTML_OPTION_SYNC) 99 | { 100 | options.setTextMode(); 101 | // exp.setSyncLoading(); 102 | } 103 | else 104 | { 105 | if (asyncLoading != HTML_OPTION_ONINIT) 106 | { 107 | options.setLoadingDataOnScroll(); 108 | } 109 | } 110 | } 111 | if (outputFilter) 112 | { 113 | options.supportsFilter(); 114 | } 115 | options.filterByName(); 116 | 117 | exp.setOptions(options); 118 | 119 | std::map> usersAndSessions; 120 | 121 | std::map mapSessions; 122 | 123 | for (auto it = sessions.cbegin(); it != sessions.cend(); ++it) 124 | { 125 | mapSessions[*it] = NULL; 126 | } 127 | 128 | usersAndSessions[account] = mapSessions; 129 | 130 | exp.filterUsersAndSessions(usersAndSessions); 131 | 132 | exp.run(); 133 | 134 | exp.waitForComplition(); 135 | 136 | return 0; 137 | } 138 | 139 | #endif // WechatExporterCmd_h 140 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 微信聊天记录导出程序 Wechat Exporter 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |

微信聊天记录导出程序 Wechat Exporter (for Windows/MacOS)

17 |

C++源码:https://github.com/BlueMatthew/WechatExporter

18 |

操作步骤

19 |
    20 |
  1. 通过iTunes将手机备份到电脑上(建议备份前杀掉微信),Windows操作系统一般位于目录:C:\用户[用户名]\AppData\Roaming\Apple Computer\MobileSync\Backup\。Android手机可以找一个iPad/iPhone设备,把聊天记录迁移到iPad/iPhone设备上,然后通过iTunes备份到电脑上。

  2. 21 |
  3. 下载本代码的执行文件:Windows x64版本 或者 MacOS x64版本,然后解压压缩文件

  4. 22 |
  5. 执行解压出来的WechatExport.exe/WechatExporter (Windows下如果运行报缺少必须的dll文件,请安装Visual C++ 2017 redist后再尝试运行)

  6. 23 |
  7. 按界面提示进行操作。
    24 | Windows界面截屏 25 |
    26 | MacOS界面截屏

  8. 27 |
28 |

模版修改

29 |

解压目录下的res(MacOS版本位于Contents)子目录里存放了输出聊天记录的html页面模版,其中通过两个%包含起来的字符串,譬如,%%NAME%%,不要修改之外,其它页面内容和格式都可以自行调整。

30 |

系统依赖

31 |

Windows版本:Windows 7+, Visual C++ 2017 redist at The latest supported Visual C++ downloads
MacOS版本:MacOS 10.10(Yosemite)+

32 |

程序编译

33 |

程序依赖如下第三方库:
34 | - libxml2: http://www.xmlsoft.org/
35 | - libcurl: https://curl.se/libcurl/
36 | - libsqlite3: https://www.sqlite.org/index.html
37 | - libprotobuf: https://github.com/protocolbuffers/protobuf
38 | - libjsoncpp: https://github.com/open-source-parsers/jsoncpp
39 | - lame: http://lame.sourceforge.net/
40 | - silk: https://github.com/collects/silk (也参考了: https://github.com/kn007/silk-v3-decoder)
41 | - libplist: https://github.com/libimobiledevice/libplist https://github.com/libimobiledevice-win32/libplist
42 | - libiconv(windows only): https://www.gnu.org/software/libiconv/
43 | - openssl(windows only):https://github.com/openssl/openssl
44 | - WTL (windows only):https://sourceforge.net/projects/wtl/

45 |

MacOS下,libxml2,libcurl,libsqlite3直接使用了Xcode自带的库,其它第三方库需自行编译。
libmp3lame需手动删除文件include/libmp3lame.sym中的行:lame_init_old

46 |

Windows环境下,silk自带Visual Studio工程文件,可以直接利用Visual Studio编译,其余除了libplist之外,都通过vcpkg可以编译。libplist在vcpkg中也存在,但是在编译x64-windows-static target的时候报了错,于是直接通过Visual Studio建了工程进行编译。可以直接下载预编译好的静态库文件

47 |

 

48 |

 

49 | 50 | -------------------------------------------------------------------------------- /docs/screenshots/mac.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/docs/screenshots/mac.png -------------------------------------------------------------------------------- /docs/screenshots/screenshot-mac.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/docs/screenshots/screenshot-mac.png -------------------------------------------------------------------------------- /docs/screenshots/screenshot-win.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/docs/screenshots/screenshot-win.png -------------------------------------------------------------------------------- /docs/screenshots/win.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/docs/screenshots/win.png -------------------------------------------------------------------------------- /docs/update.conf: -------------------------------------------------------------------------------- 1 | 1.8.0.8 2 | https://src.wakin.org/github/wxexp/ 3 | https://src.wakin.org/github/wxexp/ -------------------------------------------------------------------------------- /libplist.README.md: -------------------------------------------------------------------------------- 1 | the libplist is for win32 from vcpkg as vcpkg install fails on x64-windows-static 2 | it should be from: https://github.com/libimobiledevice-win32/libplist 3 | 4 | For Mac OS, please use the code at: https://github.com/libimobiledevice/libplist -------------------------------------------------------------------------------- /release: -------------------------------------------------------------------------------- 1 | releases -------------------------------------------------------------------------------- /vcproject/AboutDlg.h: -------------------------------------------------------------------------------- 1 | // aboutdlg.h : interface of the CAboutDlg class 2 | // 3 | ///////////////////////////////////////////////////////////////////////////// 4 | 5 | #pragma once 6 | #include 7 | #include "VersionDetector.h" 8 | 9 | class CAboutDlg : public CDialogImpl 10 | { 11 | public: 12 | enum { IDD = IDD_ABOUTBOX }; 13 | 14 | BEGIN_MSG_MAP(CAboutDlg) 15 | MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog) 16 | COMMAND_ID_HANDLER(IDOK, OnCloseCmd) 17 | COMMAND_ID_HANDLER(IDCANCEL, OnCloseCmd) 18 | END_MSG_MAP() 19 | 20 | // Handler prototypes (uncomment arguments if needed): 21 | // LRESULT MessageHandler(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) 22 | // LRESULT CommandHandler(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) 23 | // LRESULT NotifyHandler(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/) 24 | 25 | LRESULT OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) 26 | { 27 | m_homePageLinkCtrl.SubclassWindow(GetDlgItem(IDC_VERSION)); 28 | // Replace to current version 29 | // If the version is set in aboud dlg resource directly, 30 | // The code below won't bring error. 31 | // CStatic lblVersion = GetDlgItem(IDC_VERSION); 32 | CString version; 33 | m_homePageLinkCtrl.GetWindowText(version); 34 | VersionDetector vd; 35 | CString newVersion = vd.GetProductVersion(); 36 | newVersion = TEXT("v") + newVersion; 37 | version.Replace(TEXT("v1.0"), newVersion); 38 | m_homePageLinkCtrl.SetLabel(version); 39 | 40 | m_homePageLinkCtrl.SetHyperLink(TEXT("https://github.com/BlueMatthew/WechatExporter")); 41 | 42 | CenterWindow(GetParent()); 43 | return TRUE; 44 | } 45 | 46 | LRESULT OnCloseCmd(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/) 47 | { 48 | EndDialog(wID); 49 | return 0; 50 | } 51 | 52 | protected: 53 | CHyperLink m_homePageLinkCtrl; 54 | }; 55 | -------------------------------------------------------------------------------- /vcproject/AppConfiguration.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #define ASYNC_NONE 0 6 | #define ASYNC_ONSCROLL 1 7 | #define ASYNC_PAGER_NORMAL 2 8 | #define ASYNC_PAGER_ON_YEAR 3 9 | #define ASYNC_PAGER_ON_MONTH 4 10 | 11 | class AppConfiguration 12 | { 13 | public: 14 | 15 | enum { OUTPUT_FORMAT_HTML = 0, OUTPUT_FORMAT_TEXT, OUTPUT_FORMAT_PDF, OUTPUT_FORMAT_LAST }; 16 | 17 | static void SetDescOrder(BOOL descOrder); 18 | static BOOL GetDescOrder(); 19 | static UINT GetOutputFormat(); 20 | static void SetOutputFormat(UINT outputFormat); 21 | static void SetSavingInSession(BOOL savingInSession); 22 | static BOOL GetSavingInSession(); 23 | 24 | static void SetUsingRemoteEmoji(BOOL usingRemoteEmoji); 25 | static BOOL GetUsingRemoteEmoji(); 26 | 27 | static void SetIncrementalExporting(BOOL incrementalExporting); 28 | static BOOL GetIncrementalExporting(); 29 | 30 | static void SetLastOutputDir(LPCTSTR szOutputDir); 31 | static CString GetLastOrDefaultOutputDir(); 32 | static CString GetDefaultOutputDir(); 33 | 34 | static void SetLastBackupDir(LPCTSTR szBackupDir); 35 | static CString GetLastBackupDir(); 36 | static CString GetDefaultBackupDir(BOOL bCheckExistence = TRUE); 37 | 38 | static DWORD GetLastCheckUpdateTime(); 39 | static void SetLastCheckUpdateTime(DWORD lastCheckUpdateTime = 0); 40 | 41 | static void SetCheckingUpdateDisabled(BOOL disabled); 42 | static BOOL GetCheckingUpdateDisabled(); 43 | 44 | static void SetLoadingDataOnScroll(); 45 | static BOOL GetLoadingDataOnScroll(); 46 | 47 | static void SetSyncLoading(); 48 | static BOOL GetSyncLoading(); 49 | static DWORD GetAsyncLoading(); 50 | static void SetAsyncLoading(DWORD asyncLoading); 51 | 52 | static void SetNormalPagination(); 53 | static BOOL GetNormalPagination(); 54 | static void SetPaginationOnYear(); 55 | static BOOL GetPaginationOnYear(); 56 | static void SetPaginationOnMonth(); 57 | static BOOL GetPaginationOnMonth(); 58 | 59 | static void SetSupportingFilter(BOOL supportingFilter); 60 | static BOOL GetSupportingFilter(); 61 | 62 | static void SetOutputDebugLogs(BOOL dbgLogs); 63 | static BOOL OutputDebugLogs(); 64 | 65 | static void SetIncludingSubscriptions(BOOL includingSubscriptions); 66 | static BOOL IncludeSubscriptions(); 67 | 68 | static void SetOpenningFolderAfterExp(BOOL openningFolderAfterExp); 69 | static BOOL GetOpenningFolderAfterExp(); 70 | 71 | static BOOL IsPdfSupported(); 72 | 73 | static void upgrade(); 74 | static uint64_t BuildOptions(); 75 | 76 | protected: 77 | static BOOL GetStringProperty(LPCTSTR name, CString& value); 78 | static BOOL SetStringProperty(LPCTSTR name, LPCTSTR value); 79 | 80 | static BOOL GetDwordProperty(LPCTSTR name, DWORD& value); 81 | static BOOL SetDwordProperty(LPCTSTR name, DWORD value); 82 | static DWORD GetDwordValue(LPCTSTR name, DWORD defaultValue); 83 | 84 | static BOOL IsAppInstalled(LPCTSTR name, BOOL lmOrCU); 85 | 86 | }; 87 | -------------------------------------------------------------------------------- /vcproject/BackupDlg.h: -------------------------------------------------------------------------------- 1 | // aboutdlg.h : interface of the CAboutDlg class 2 | // 3 | ///////////////////////////////////////////////////////////////////////////// 4 | 5 | #pragma once 6 | 7 | #include 8 | 9 | class CBackupDlg : public CDialogImpl 10 | { 11 | public: 12 | enum { IDD = IDD_BACKUP_DLG }; 13 | 14 | BEGIN_MSG_MAP(CBackupDlg) 15 | MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog) 16 | MESSAGE_HANDLER(WM_TIMER, OnTimer) 17 | // COMMAND_ID_HANDLER(IDOK, OnCloseCmd) 18 | MESSAGE_HANDLER(WM_DESTROY, OnDestroy) 19 | COMMAND_ID_HANDLER(IDCANCEL, OnCloseCmd) 20 | END_MSG_MAP() 21 | 22 | 23 | CBackupDlg(const DeviceInfo& deviceInfo, const CString& outputDir) : m_deviceInfo(deviceInfo), m_outputDir(outputDir), m_backup(NULL), m_eventId(0), m_result(false) 24 | { 25 | 26 | } 27 | 28 | // Handler prototypes (uncomment arguments if needed): 29 | // LRESULT MessageHandler(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) 30 | // LRESULT CommandHandler(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) 31 | // LRESULT NotifyHandler(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/) 32 | 33 | LRESULT OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) 34 | { 35 | CenterWindow(GetParent()); 36 | 37 | CProgressBarCtrl progressCtrl = GetDlgItem(IDC_PROGRESS); 38 | // progressCtrl.SetWindowText(TEXT("")); 39 | progressCtrl.ModifyStyle(PBS_MARQUEE, 0); 40 | progressCtrl.SetRange32(1, 100); 41 | progressCtrl.SetStep(1); 42 | progressCtrl.SetPos(0); 43 | 44 | CW2A outputDir(CT2W((LPCTSTR)m_outputDir), CP_UTF8); 45 | m_backup = new IDeviceBackup(m_deviceInfo, (LPCSTR)outputDir); 46 | 47 | m_task = std::async(std::launch::async, &IDeviceBackup::backup, m_backup); 48 | 49 | m_eventId = SetTimer(1, 200); 50 | return TRUE; 51 | } 52 | 53 | LRESULT OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) 54 | { 55 | if (0 != m_eventId) 56 | { 57 | KillTimer(m_eventId); 58 | } 59 | CenterWindow(GetParent()); 60 | 61 | return 0; 62 | } 63 | 64 | LRESULT OnTimer(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) 65 | { 66 | if (NULL != m_backup) 67 | { 68 | double progress = m_backup->getOverallProgress(); 69 | int pos = (int)(progress * 100); 70 | if (pos > 100) 71 | { 72 | pos = 100; 73 | } 74 | else if (pos < 0) 75 | { 76 | pos = 0; 77 | } 78 | 79 | CProgressBarCtrl progressCtrl = GetDlgItem(IDC_PROGRESS); 80 | if (pos != progressCtrl.GetPos()) 81 | { 82 | progressCtrl.SetPos(pos); 83 | } 84 | } 85 | std::future_status status = m_task.wait_for(std::chrono::seconds(0)); 86 | if (status == std::future_status::ready) 87 | { 88 | KillTimer(m_eventId); 89 | m_eventId = 0; 90 | 91 | m_result = m_task.get(); 92 | 93 | EndDialog(IDOK); 94 | } 95 | 96 | 97 | return 0; 98 | } 99 | 100 | LRESULT OnCloseCmd(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/) 101 | { 102 | EndDialog(wID); 103 | return 0; 104 | } 105 | 106 | bool getBackupResult() const 107 | { 108 | return m_result; 109 | } 110 | 111 | protected: 112 | // CHyperLink m_homePageLinkCtrl; 113 | const DeviceInfo& m_deviceInfo; 114 | CString m_outputDir; 115 | IDeviceBackup* m_backup; 116 | std::future m_task; 117 | 118 | UINT m_eventId; 119 | 120 | bool m_result; 121 | }; 122 | -------------------------------------------------------------------------------- /vcproject/Core.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "..\WechatExporter\core\FileSystem.h" 4 | #include "..\WechatExporter\core\Logger.h" 5 | #include "..\WechatExporter\core\PdfConverter.h" 6 | #include "..\WechatExporter\core\ExportOption.h" 7 | #include "..\WechatExporter\core\Exporter.h" 8 | #include "..\WechatExporter\core\Updater.h" 9 | #include "..\WechatExporter\core\IDeviceBackup.h" 10 | #include "..\WechatExporter\core\WechatSource.h" 11 | -------------------------------------------------------------------------------- /vcproject/ExportNotifierImpl.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "stdafx.h" 4 | #include "Core.h" 5 | 6 | class ExportNotifierImpl : public ExportNotifier 7 | { 8 | protected: 9 | HWND m_hWnd; 10 | 11 | public: 12 | static const UINT WM_START = WM_USER + 10; 13 | static const UINT WM_COMPLETE = WM_USER + 11; 14 | static const UINT WM_PROGRESS = WM_USER + 12; 15 | static const UINT WM_USR_SESS_START = WM_USER + 13; 16 | static const UINT WM_USR_SESS_COMPLETE = WM_USER + 14; 17 | static const UINT WM_SESSION_START = WM_USER + 16; 18 | static const UINT WM_SESSION_COMPLETE = WM_USER + 17; 19 | static const UINT WM_SESSION_PROGRESS = WM_USER + 18; 20 | static const UINT WM_TASKS_START = WM_USER + 19; 21 | static const UINT WM_TASKS_COMPLETE = WM_USER + 20; 22 | static const UINT WM_TASKS_PROGRESS = WM_USER + 21; 23 | static const UINT WM_EN_END = WM_TASKS_PROGRESS; 24 | 25 | public: 26 | ExportNotifierImpl(HWND hWnd) : m_hWnd(hWnd) 27 | { 28 | } 29 | 30 | ~ExportNotifierImpl() 31 | { 32 | m_hWnd = NULL; 33 | } 34 | 35 | void onStart() const 36 | { 37 | ::PostMessage(m_hWnd, WM_START, (WPARAM)0, (LPARAM)1/*cancellable*/); 38 | } 39 | 40 | void onProgress(uint32_t numberOfMessages, uint32_t numberOfTotalMessages) const 41 | { 42 | ::PostMessage(m_hWnd, WM_PROGRESS, (WPARAM)numberOfMessages, (LPARAM)numberOfTotalMessages); 43 | } 44 | 45 | void onComplete(bool cancelled) const 46 | { 47 | ::PostMessage(m_hWnd, WM_COMPLETE, (WPARAM)0, (LPARAM)(cancelled ? 1 : 0)); 48 | } 49 | 50 | void onUserSessionStart(const std::string& usrName, uint32_t numberOfSessions) const 51 | { 52 | 53 | } 54 | 55 | void onUserSessionComplete(const std::string& usrName) const 56 | { 57 | 58 | } 59 | 60 | void onSessionStart(const std::string& sessionUsrName, void * sessionData, uint32_t numberOfTotalMessages) const 61 | { 62 | ::PostMessage(m_hWnd, WM_SESSION_START, reinterpret_cast(sessionData), (LPARAM)numberOfTotalMessages); 63 | } 64 | 65 | void onSessionProgress(const std::string& sessionUsrName, void * sessionData, uint32_t numberOfMessages, uint32_t numberOfTotalMessages) const 66 | { 67 | ::PostMessage(m_hWnd, WM_SESSION_PROGRESS, reinterpret_cast(sessionData), (LPARAM)numberOfMessages); 68 | } 69 | 70 | void onSessionComplete(const std::string& sessionUsrName, void * sessionData, bool cancelled) const 71 | { 72 | ::PostMessage(m_hWnd, WM_SESSION_COMPLETE, reinterpret_cast(sessionData), (LPARAM)(cancelled ? 1 : 0)); 73 | } 74 | 75 | void onTasksStart(const std::string& usrName, uint32_t numberOfTotalTasks) const 76 | { 77 | ::PostMessage(m_hWnd, WM_TASKS_START, (WPARAM)numberOfTotalTasks, 0); 78 | } 79 | 80 | void onTasksProgress(const std::string& usrName, uint32_t numberOfCompletedTasks, uint32_t numberOfTotalMessages) const 81 | { 82 | ::PostMessage(m_hWnd, WM_TASKS_PROGRESS, numberOfTotalMessages, (LPARAM)numberOfCompletedTasks); 83 | } 84 | 85 | void onTasksComplete(const std::string& usrName, bool cancelled) const 86 | { 87 | ::PostMessage(m_hWnd, WM_TASKS_COMPLETE, 0, (LPARAM)(cancelled ? 1 : 0)); 88 | } 89 | }; 90 | 91 | -------------------------------------------------------------------------------- /vcproject/LogListBox.h: -------------------------------------------------------------------------------- 1 | #if !defined(AFX_LOGLISTBOX_H_INCLUDED_) 2 | #define AFX_LOGLISTBOX_H_INCLUDED_ 3 | 4 | #include 5 | #include 6 | #pragma once 7 | 8 | #ifndef __cplusplus 9 | #error WTL requires C++ compilation (use a .cpp suffix) 10 | #endif 11 | 12 | #ifndef __ATLAPP_H__ 13 | #error LogListBox.h requires atlapp.h to be included first 14 | #endif 15 | 16 | #ifndef __ATLCTRLS_H__ 17 | #error LogListBox.h requires atlctrls.h to be included first 18 | #endif 19 | 20 | template 21 | class ATL_NO_VTABLE CLogListBoxImpl : public ATL::CWindowImpl< T, TBase, TWinTraits > 22 | { 23 | public: 24 | // DECLARE_WND_SUPERCLASS(_T("WTL_LogListBox"), GetWndClassName()) 25 | DECLARE_WND_SUPERCLASS2(NULL, T, TBase::GetWndClassName()) 26 | // DECLARE_WND_SUPERCLASS2(NULL, CLogListBox, CListBox::GetWndClassName()) 27 | // DECLARE_WND_SUPERCLASS(_T("WTL_LogListBox"), GetWndClassName()) 28 | 29 | // Message map 30 | BEGIN_MSG_MAP(CLogListBoxImpl) 31 | MESSAGE_HANDLER(WM_KEYDOWN, OnKeyDown) 32 | END_MSG_MAP() 33 | 34 | // Message handlers 35 | LRESULT OnKeyDown(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 36 | { 37 | if ((::GetKeyState(VK_CONTROL) & 0x8000) != 0) 38 | { 39 | if (wParam == 'A' || wParam == 'a') 40 | { 41 | SetSel(-1); 42 | } 43 | else if (wParam == 'C' || wParam == 'c') 44 | { 45 | int selCount = GetSelCount(); 46 | if (selCount > 0) 47 | { 48 | std::vector rgIndex(selCount, 0); 49 | GetSelItems(selCount, &rgIndex[0]); 50 | CString contents; 51 | CString line; 52 | for (int idx = 0; idx < selCount; ++idx) 53 | { 54 | GetText(rgIndex[idx], line); 55 | contents.Append(line); 56 | contents.Append(TEXT("\r\n")); 57 | } 58 | 59 | int cch = contents.GetLength(); 60 | if (cch > 0 && ::OpenClipboard(NULL)) 61 | { 62 | ::EmptyClipboard(); 63 | 64 | HGLOBAL hglbCopy = ::GlobalAlloc(GMEM_MOVEABLE, (cch + 1) * sizeof(TCHAR)); 65 | if (NULL != hglbCopy) 66 | { 67 | // Lock the handle and copy the text to the buffer. 68 | LPTSTR lptstrCopy = (LPTSTR)::GlobalLock(hglbCopy); 69 | LPTSTR lptstrBuffer = contents.LockBuffer(); 70 | memcpy(lptstrCopy, lptstrBuffer, cch * sizeof(TCHAR)); 71 | contents.UnlockBuffer(); 72 | lptstrCopy[cch] = (TCHAR)0; // null character 73 | ::GlobalUnlock(hglbCopy); 74 | // Place the handle on the clipboard. 75 | 76 | #ifdef _UNICODE 77 | ::SetClipboardData(CF_UNICODETEXT, hglbCopy); 78 | #else 79 | ::SetClipboardData(CF_TEXT, hglbCopy); 80 | #endif 81 | 82 | } 83 | 84 | ::CloseClipboard(); 85 | if (NULL != hglbCopy) ::GlobalFree(hglbCopy); 86 | } 87 | } 88 | } 89 | } 90 | 91 | return 0; 92 | } 93 | 94 | }; 95 | 96 | 97 | 98 | class CLogListBox : public CLogListBoxImpl 99 | { 100 | public: 101 | DECLARE_WND_SUPERCLASS(_T("WTL_LogListBox"), GetWndClassName()) 102 | 103 | }; 104 | 105 | 106 | #endif // !defined(AFX_LOGLISTBOX_H_INCLUDED_) 107 | -------------------------------------------------------------------------------- /vcproject/LoggerImpl.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "stdafx.h" 4 | #include "Core.h" 5 | #include 6 | #include 7 | 8 | class LoggerImpl : public Logger 9 | { 10 | protected: 11 | #if !defined(NDEBUG) || defined(DBG_PERF) 12 | CRITICAL_SECTION m_cs; 13 | TCHAR m_szLogFile[MAX_PATH]; 14 | #endif 15 | HWND m_hWndLog; 16 | public: 17 | LoggerImpl(HWND hWndLog) : m_hWndLog(hWndLog) 18 | { 19 | #if !defined(NDEBUG) || defined(DBG_PERF) 20 | InitializeCriticalSection(&m_cs); 21 | m_szLogFile[0] = 0; 22 | #endif 23 | } 24 | 25 | ~LoggerImpl() 26 | { 27 | m_hWndLog = NULL; 28 | #if !defined(NDEBUG) || defined(DBG_PERF) 29 | DeleteCriticalSection(&m_cs); 30 | #endif 31 | } 32 | 33 | void setLogPath(LPCTSTR lpszLogPath) 34 | { 35 | #if !defined(NDEBUG) || defined(DBG_PERF) 36 | EnterCriticalSection(&m_cs); 37 | _tcscpy(m_szLogFile, lpszLogPath); 38 | PathAppend(m_szLogFile, TEXT("log.txt")); 39 | HANDLE hFile = CreateFile(m_szLogFile, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); 40 | if (INVALID_HANDLE_VALUE != hFile) 41 | { 42 | CloseHandle(hFile); 43 | } 44 | LeaveCriticalSection(&m_cs); 45 | #endif 46 | } 47 | 48 | void outputLog(LPCTSTR pszLog) 49 | { 50 | ::SendMessage(m_hWndLog, LB_ADDSTRING, 0, (LPARAM)pszLog); 51 | LRESULT count = ::SendMessage(m_hWndLog, LB_GETCOUNT, 0, 0L); 52 | ::SendMessage(m_hWndLog, LB_SETTOPINDEX, count - 1, 0L); 53 | } 54 | 55 | void write(const std::string& log) 56 | { 57 | #if !defined(NDEBUG) || defined(DBG_PERF) 58 | std::string timeString = getTimestampString(false, true) + ": "; 59 | #else 60 | std::string timeString = getTimestampString() + ": "; 61 | #endif 62 | CA2T szTime(timeString.c_str()); 63 | 64 | #if !defined(NDEBUG) || defined(DBG_PERF) 65 | EnterCriticalSection(&m_cs); 66 | if (_tcslen(m_szLogFile) > 0) 67 | { 68 | HANDLE hFile = ::CreateFile(m_szLogFile, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); 69 | if (INVALID_HANDLE_VALUE != hFile) 70 | { 71 | ::SetFilePointer(hFile, 0, 0, FILE_END); 72 | DWORD dwBytesToWrite = static_cast(log.size()); 73 | DWORD dwBytesWritten = 0; 74 | BOOL bErrorFlag = WriteFile(hFile, timeString.c_str(), timeString.size(), &dwBytesWritten, NULL); 75 | bErrorFlag = WriteFile(hFile, log.c_str(), dwBytesToWrite, &dwBytesWritten, NULL); 76 | if (bErrorFlag) 77 | { 78 | WriteFile(hFile, "\r\n", 2, &dwBytesWritten, NULL); 79 | } 80 | CloseHandle(hFile); 81 | } 82 | } 83 | LeaveCriticalSection(&m_cs); 84 | #endif 85 | 86 | CW2T pszT(CA2W(log.c_str(), CP_UTF8)); 87 | 88 | std::vector szLog; 89 | szLog.resize(_tcslen(pszT) + _tcslen(szTime) + 1, 0); 90 | 91 | _tcscpy(&szLog[0], (LPCTSTR)szTime); 92 | _tcscat(&szLog[0], (LPCTSTR)pszT); 93 | 94 | outputLog(&szLog[0]); 95 | } 96 | 97 | void debug(const std::string& log) 98 | { 99 | // #if !defined(NDEBUG) || defined(DBG_PERF) 100 | write("[DBG] " + log); 101 | // #endif 102 | } 103 | }; 104 | 105 | -------------------------------------------------------------------------------- /vcproject/TextProgressBarCtrl.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | template 8 | class ATL_NO_VTABLE CTextProgressBarCtrlT : public CWindowImpl, public CThemeImpl 9 | { 10 | public: 11 | CTextProgressBarCtrlT() 12 | { 13 | SetThemeClassList(L"PROGRESS"); 14 | } 15 | 16 | BOOL SubclassWindow(HWND hWnd) 17 | { 18 | BOOL result = CWindowImpl::SubclassWindow(hWnd); 19 | if (result) 20 | { 21 | if (GetThemeClassList() != NULL) 22 | OpenThemeData(); 23 | } 24 | 25 | return result; 26 | } 27 | 28 | BEGIN_MSG_MAP(CTextProgressBarCtrl) 29 | CHAIN_MSG_MAP(CThemeImpl) 30 | MESSAGE_HANDLER(WM_PAINT, OnPaint) 31 | MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBkgnd) 32 | DEFAULT_REFLECTION_HANDLER() 33 | END_MSG_MAP() 34 | 35 | // message handlers 36 | 37 | LRESULT OnPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 38 | { 39 | RECT rc; 40 | GetClientRect(&rc); 41 | 42 | CPaintDC dc(m_hWnd); 43 | 44 | if (rc.right > rc.left && rc.bottom > rc.top) 45 | { 46 | CMemoryDC dcMem(dc.m_hDC, rc); 47 | 48 | if (IsAppThemed()) 49 | DrawThemedProgressBar(dcMem, rc); 50 | else 51 | DrawClassicProgressBar(dcMem, rc); 52 | 53 | DrawText(dcMem, rc); 54 | } 55 | 56 | bHandled = TRUE; 57 | return 1; 58 | } 59 | 60 | LRESULT OnEraseBkgnd(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 61 | { 62 | return 1; // we painted the background 63 | } 64 | 65 | 66 | protected: 67 | 68 | void CalcFillRect(CRect& rc) 69 | { 70 | int nLower = 0, nUpper = 0; 71 | GetRange(nLower, nUpper); 72 | if (nLower >= nUpper) nLower = nUpper - 1; 73 | int nPos = GetPos(); 74 | 75 | if (nPos < nLower) nPos = nLower; 76 | if (nPos > nUpper) nPos = nUpper; 77 | 78 | DWORD dwStyle = GetStyle(); 79 | BOOL bVertical = (dwStyle & PBS_VERTICAL) == PBS_VERTICAL; 80 | 81 | if (bVertical) 82 | rc.top = rc.bottom - (rc.bottom - rc.top) * (nPos - nLower) / (nUpper - nLower); 83 | else 84 | rc.right = rc.left + (rc.right - rc.left) * (nPos - nLower) / (nUpper - nLower); 85 | } 86 | 87 | void DrawThemedProgressBar(CMemoryDC& dc, const CRect& rc) 88 | { 89 | DrawThemeBackground(dc, PP_BAR, 0, &rc, NULL); 90 | 91 | CRect rcFill = rc; 92 | CalcFillRect(rcFill); 93 | DrawThemeBackground(dc, PP_FILL, PBFS_NORMAL, &rcFill, NULL); 94 | } 95 | 96 | void DrawClassicProgressBar(CMemoryDC& dc, const CRect& rc) 97 | { 98 | CRect rcEdge = rc; 99 | dc.DrawEdge(&rcEdge, EDGE_ETCHED, BF_RECT); 100 | 101 | CRect rc2 = rc; 102 | rc2.DeflateRect(1, 1, 1, 1); 103 | dc.FillSolidRect(&rc2, ::GetSysColor(COLOR_BTNFACE)); 104 | 105 | CRect rcFill = rc2; 106 | if (rcFill.right > rcFill.left && rcFill.bottom > rcFill.top) 107 | { 108 | CalcFillRect(rcFill); 109 | dc.FillSolidRect(&rcFill, ::GetSysColor(COLOR_HIGHLIGHT)); 110 | } 111 | } 112 | 113 | // draw text on the bar 114 | void DrawText(CMemoryDC& dc, const CRect& rc) 115 | { 116 | CString text; 117 | if (GetWindowText(text) > 0) 118 | { 119 | int oldBkMode = dc.SetBkMode(TRANSPARENT); 120 | 121 | HFONT hFont = GetFont(); 122 | if (NULL == hFont) 123 | { 124 | hFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT); 125 | } 126 | HFONT oldFont = dc.SelectFont(hFont); 127 | CRect rcText = rc; 128 | dc.DrawText((LPCTSTR)text, -1, &rcText, DT_CENTER | DT_VCENTER | DT_SINGLELINE); 129 | dc.SelectFont(oldFont); 130 | 131 | dc.SetBkMode(oldBkMode); 132 | } 133 | } 134 | }; 135 | 136 | class CTextProgressBarCtrl : public CTextProgressBarCtrlT 137 | { 138 | public: 139 | DECLARE_WND_SUPERCLASS(_T("WTL_progressbar"), GetWndClassName()) 140 | }; 141 | -------------------------------------------------------------------------------- /vcproject/ToolTipButton.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | template 4 | class ATL_NO_VTABLE CToolTipButtonImpl : public ATL::CWindowImpl< T, TBase, TWinTraits > 5 | { 6 | public: 7 | DECLARE_WND_SUPERCLASS2(NULL, T, TBase::GetWndClassName()) 8 | 9 | enum 10 | { 11 | _nImageNormal = 0, 12 | _nImagePushed, 13 | _nImageFocusOrHover, 14 | _nImageDisabled, 15 | 16 | _nImageCount = 4, 17 | }; 18 | 19 | enum 20 | { 21 | ID_TIMER_FIRST = 1000, 22 | ID_TIMER_REPEAT = 1001 23 | }; 24 | 25 | CToolTipCtrl m_tip; 26 | LPTSTR m_lpstrToolTipText; 27 | 28 | // Constructor/Destructor 29 | CToolTipButtonImpl() : 30 | m_lpstrToolTipText(NULL) 31 | { 32 | 33 | } 34 | 35 | ~CToolTipButtonImpl() 36 | { 37 | delete[] m_lpstrToolTipText; 38 | } 39 | 40 | // overridden to provide proper initialization 41 | BOOL SubclassWindow(HWND hWnd) 42 | { 43 | BOOL bRet = ATL::CWindowImpl< T, TBase, TWinTraits >::SubclassWindow(hWnd); 44 | if (bRet != FALSE) 45 | { 46 | T* pT = static_cast(this); 47 | pT->Init(); 48 | } 49 | 50 | return bRet; 51 | } 52 | 53 | // Attributes 54 | 55 | int GetToolTipTextLength() const 56 | { 57 | return (m_lpstrToolTipText == NULL) ? -1 : lstrlen(m_lpstrToolTipText); 58 | } 59 | 60 | bool GetToolTipText(LPTSTR lpstrText, int nLength) const 61 | { 62 | ATLASSERT(lpstrText != NULL); 63 | if (m_lpstrToolTipText == NULL) 64 | return false; 65 | 66 | errno_t nRet = ATL::Checked::tcsncpy_s(lpstrText, nLength, m_lpstrToolTipText, _TRUNCATE); 67 | 68 | return ((nRet == 0) || (nRet == STRUNCATE)); 69 | } 70 | 71 | bool SetToolTipText(LPCTSTR lpstrText) 72 | { 73 | if (m_lpstrToolTipText != NULL) 74 | { 75 | delete[] m_lpstrToolTipText; 76 | m_lpstrToolTipText = NULL; 77 | } 78 | 79 | if (lpstrText == NULL) 80 | { 81 | if (m_tip.IsWindow()) 82 | m_tip.Activate(FALSE); 83 | return true; 84 | } 85 | 86 | int cchLen = lstrlen(lpstrText) + 1; 87 | ATLTRY(m_lpstrToolTipText = new TCHAR[cchLen]); 88 | if (m_lpstrToolTipText == NULL) 89 | return false; 90 | 91 | ATL::Checked::tcscpy_s(m_lpstrToolTipText, cchLen, lpstrText); 92 | if (m_tip.IsWindow()) 93 | { 94 | m_tip.Activate(TRUE); 95 | m_tip.AddTool(this->m_hWnd, m_lpstrToolTipText); 96 | } 97 | 98 | return true; 99 | } 100 | 101 | // Overrideables 102 | 103 | // Message map and handlers 104 | BEGIN_MSG_MAP(CBitmapButtonImpl) 105 | MESSAGE_HANDLER(WM_CREATE, OnCreate) 106 | MESSAGE_HANDLER(WM_DESTROY, OnDestroy) 107 | MESSAGE_RANGE_HANDLER(WM_MOUSEFIRST, WM_MOUSELAST, OnMouseMessage) 108 | // MESSAGE_HANDLER(WM_MOUSEMOVE, OnMouseMove) 109 | // MESSAGE_HANDLER(WM_MOUSELEAVE, OnMouseLeave) 110 | END_MSG_MAP() 111 | 112 | LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled) 113 | { 114 | T* pT = static_cast(this); 115 | pT->Init(); 116 | 117 | bHandled = FALSE; 118 | return 1; 119 | } 120 | 121 | LRESULT OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled) 122 | { 123 | if (m_tip.IsWindow()) 124 | { 125 | m_tip.DestroyWindow(); 126 | m_tip.m_hWnd = NULL; 127 | } 128 | bHandled = FALSE; 129 | return 1; 130 | } 131 | 132 | LRESULT OnMouseMessage(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 133 | { 134 | MSG msg = { this->m_hWnd, uMsg, wParam, lParam }; 135 | if (m_tip.IsWindow()) 136 | m_tip.RelayEvent(&msg); 137 | bHandled = FALSE; 138 | return 1; 139 | } 140 | 141 | // Implementation 142 | void Init() 143 | { 144 | 145 | // create a tool tip 146 | m_tip.Create(this->m_hWnd); 147 | ATLASSERT(m_tip.IsWindow()); 148 | if (m_tip.IsWindow() && (m_lpstrToolTipText != NULL)) 149 | { 150 | m_tip.Activate(TRUE); 151 | m_tip.AddTool(this->m_hWnd, m_lpstrToolTipText); 152 | } 153 | 154 | } 155 | 156 | BOOL StartTrackMouseLeave() 157 | { 158 | TRACKMOUSEEVENT tme = {}; 159 | tme.cbSize = sizeof(tme); 160 | tme.dwFlags = TME_LEAVE; 161 | tme.hwndTrack = this->m_hWnd; 162 | return ::TrackMouseEvent(&tme); 163 | } 164 | }; 165 | 166 | class CToolTipButton : public CToolTipButtonImpl 167 | { 168 | public: 169 | DECLARE_WND_SUPERCLASS(_T("WTL_ToolTipButton"), GetWndClassName()) 170 | 171 | CToolTipButton() : 172 | CToolTipButtonImpl() 173 | { } 174 | }; 175 | -------------------------------------------------------------------------------- /vcproject/VersionDetector.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "stdafx.h" 4 | 5 | class VersionDetector 6 | { 7 | public: 8 | CString GetProductVersion() 9 | { 10 | TCHAR szPath[MAX_PATH] = { 0 }; 11 | 12 | GetModuleFileName(NULL, szPath, MAX_PATH); 13 | 14 | return GetProductVersion(szPath); 15 | } 16 | 17 | CString GetProductVersion(LPCTSTR szPath) 18 | { 19 | CString strResult; 20 | 21 | DWORD dwHandle; 22 | DWORD dwSize = GetFileVersionInfoSize(szPath, &dwHandle); 23 | 24 | if (dwSize > 0) 25 | { 26 | BYTE* pbBuf = new BYTE[dwSize]; 27 | if (GetFileVersionInfo(szPath, dwHandle, dwSize, pbBuf)) 28 | { 29 | UINT uiSize; 30 | BYTE* lpb; 31 | if (VerQueryValue(pbBuf, 32 | TEXT("\\VarFileInfo\\Translation"), 33 | (void**)&lpb, 34 | &uiSize)) 35 | { 36 | WORD* lpw = (WORD*)lpb; 37 | CString strQuery; 38 | strQuery.Format(TEXT("\\StringFileInfo\\%04x%04x\\ProductVersion"), lpw[0], lpw[1]); 39 | if (VerQueryValue(pbBuf, 40 | const_cast((LPCTSTR)strQuery), 41 | (void**)&lpb, 42 | &uiSize) && uiSize > 0) 43 | { 44 | strResult = (LPCTSTR)lpb; 45 | } 46 | } 47 | } 48 | delete[] pbBuf; 49 | } 50 | 51 | return strResult; 52 | } 53 | }; -------------------------------------------------------------------------------- /vcproject/ViewHelper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include // for COM smart pointers 5 | #include // for COM smart pointers 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #define MB_TIMEDOUT 32000 12 | 13 | int MessageBoxTimeoutA(IN HWND hWnd, IN LPCSTR lpText, 14 | IN LPCSTR lpCaption, IN UINT uType, 15 | IN WORD wLanguageId, IN DWORD dwMilliseconds); 16 | int MessageBoxTimeoutW(IN HWND hWnd, IN LPCWSTR lpText, 17 | IN LPCWSTR lpCaption, IN UINT uType, 18 | IN WORD wLanguageId, IN DWORD dwMilliseconds); 19 | 20 | #ifdef UNICODE 21 | #define MessageBoxTimeout MessageBoxTimeoutW 22 | #else 23 | #define MessageBoxTimeout MessageBoxTimeoutA 24 | #endif 25 | int MsgBox(HWND hWnd, UINT uStrId, UINT uType = MB_OK); 26 | int MsgBox(HWND hWnd, const CString& text, UINT uType = MB_OK); 27 | void SetComboBoxCurSel(HWND hWnd, CComboBox &cbm, int nCurSel); 28 | void CheckAllItems(CListViewCtrl& listViewCtrl, BOOL fChecked); 29 | void SetHeaderCheckState(CListViewCtrl& listViewCtrl, BOOL fChecked); 30 | BOOL SetHeaderCheckState(CListViewCtrl& listViewCtrl); 31 | 32 | template< typename T > 33 | void ThrowIfFailed(HRESULT hr, T&& msg) 34 | { 35 | if (FAILED(hr)) 36 | throw std::system_error{ hr, std::system_category(), std::forward(msg) }; 37 | } 38 | 39 | // Deleter for a PIDL allocated by the shell. 40 | struct CoTaskMemDeleter 41 | { 42 | void operator()(ITEMIDLIST* pidl) const { ::CoTaskMemFree(pidl); } 43 | }; 44 | // A smart pointer for PIDLs. 45 | using UniquePidlPtr = std::unique_ptr< ITEMIDLIST, CoTaskMemDeleter >; 46 | BOOL OpenFolder(LPCTSTR szFolder); 47 | -------------------------------------------------------------------------------- /vcproject/WechatExporter.cpp: -------------------------------------------------------------------------------- 1 | // WechatExporter.cpp : main source file for WechatExporter.exe 2 | // 3 | 4 | #include "stdafx.h" 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "ToolTipButton.h" 11 | 12 | #include "resource.h" 13 | 14 | #include "Core.h" 15 | 16 | #include "BackupDlg.h" 17 | #include "View.h" 18 | #include "aboutdlg.h" 19 | #include "MainFrm.h" 20 | 21 | CAppModule _Module; 22 | 23 | int Run(LPTSTR /*lpstrCmdLine*/ = NULL, int nCmdShow = SW_SHOWDEFAULT) 24 | { 25 | CMessageLoop theLoop; 26 | _Module.AddMessageLoop(&theLoop); 27 | 28 | CMainFrame wndMain; 29 | 30 | if(wndMain.CreateEx() == NULL) 31 | { 32 | ATLTRACE(_T("Main window creation failed!\n")); 33 | return 0; 34 | } 35 | 36 | wndMain.ShowWindow(nCmdShow); 37 | 38 | int nRet = theLoop.Run(); 39 | 40 | _Module.RemoveMessageLoop(); 41 | return nRet; 42 | } 43 | 44 | int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPTSTR lpstrCmdLine, int nCmdShow) 45 | { 46 | HRESULT hRes = ::CoInitialize(NULL); 47 | ATLASSERT(SUCCEEDED(hRes)); 48 | 49 | if (GetUserDefaultUILanguage() == MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED)) 50 | { 51 | ::SetThreadUILanguage(MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED)); 52 | } 53 | else 54 | { 55 | ::SetThreadUILanguage(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US)); 56 | } 57 | 58 | AtlInitCommonControls(ICC_PROGRESS_CLASS | ICC_BAR_CLASSES | ICC_LISTVIEW_CLASSES | ICC_USEREX_CLASSES); // add flags to support other controls 59 | 60 | hRes = _Module.Init(NULL, hInstance); 61 | ATLASSERT(SUCCEEDED(hRes)); 62 | 63 | TCHAR buffer[MAX_PATH] = { 0 }; 64 | DWORD dwRet = GetModuleFileName(NULL, buffer, MAX_PATH); 65 | if (dwRet > 0) 66 | { 67 | if (PathRemoveFileSpec(buffer)) 68 | { 69 | PathAppend(buffer, TEXT("Dlls")); 70 | SetDllDirectory(buffer); 71 | } 72 | } 73 | #ifndef NDEBUG 74 | else 75 | { 76 | assert(false); 77 | } 78 | #endif 79 | 80 | int nRet = Run(lpstrCmdLine, nCmdShow); 81 | 82 | _Module.Term(); 83 | ::CoUninitialize(); 84 | 85 | return nRet; 86 | } 87 | -------------------------------------------------------------------------------- /vcproject/WechatExporter.h: -------------------------------------------------------------------------------- 1 | // WechatExporter.h 2 | -------------------------------------------------------------------------------- /vcproject/WechatExporter.rc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/vcproject/WechatExporter.rc -------------------------------------------------------------------------------- /vcproject/WechatExporter.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.28307.1259 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WechatExporter", "WechatExporter.vcxproj", "{F57219E8-8B1F-41E3-B553-03C1884E8E4D}" 7 | EndProject 8 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WechatExporterCmd", "WechatExporterCmd.vcxproj", "{C0637D9A-3C23-495C-B661-5FDCD2169583}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|x64 = Debug|x64 13 | Debug|x86 = Debug|x86 14 | Release|x64 = Release|x64 15 | Release|x86 = Release|x86 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {F57219E8-8B1F-41E3-B553-03C1884E8E4D}.Debug|x64.ActiveCfg = Debug|x64 19 | {F57219E8-8B1F-41E3-B553-03C1884E8E4D}.Debug|x64.Build.0 = Debug|x64 20 | {F57219E8-8B1F-41E3-B553-03C1884E8E4D}.Debug|x86.ActiveCfg = Debug|Win32 21 | {F57219E8-8B1F-41E3-B553-03C1884E8E4D}.Debug|x86.Build.0 = Debug|Win32 22 | {F57219E8-8B1F-41E3-B553-03C1884E8E4D}.Release|x64.ActiveCfg = Release|x64 23 | {F57219E8-8B1F-41E3-B553-03C1884E8E4D}.Release|x64.Build.0 = Release|x64 24 | {F57219E8-8B1F-41E3-B553-03C1884E8E4D}.Release|x86.ActiveCfg = Release|Win32 25 | {F57219E8-8B1F-41E3-B553-03C1884E8E4D}.Release|x86.Build.0 = Release|Win32 26 | {C0637D9A-3C23-495C-B661-5FDCD2169583}.Debug|x64.ActiveCfg = Debug|x64 27 | {C0637D9A-3C23-495C-B661-5FDCD2169583}.Debug|x64.Build.0 = Debug|x64 28 | {C0637D9A-3C23-495C-B661-5FDCD2169583}.Debug|x86.ActiveCfg = Debug|Win32 29 | {C0637D9A-3C23-495C-B661-5FDCD2169583}.Debug|x86.Build.0 = Debug|Win32 30 | {C0637D9A-3C23-495C-B661-5FDCD2169583}.Release|x64.ActiveCfg = Release|x64 31 | {C0637D9A-3C23-495C-B661-5FDCD2169583}.Release|x64.Build.0 = Release|x64 32 | {C0637D9A-3C23-495C-B661-5FDCD2169583}.Release|x86.ActiveCfg = Release|Win32 33 | {C0637D9A-3C23-495C-B661-5FDCD2169583}.Release|x86.Build.0 = Release|Win32 34 | EndGlobalSection 35 | GlobalSection(SolutionProperties) = preSolution 36 | HideSolutionNode = FALSE 37 | EndGlobalSection 38 | GlobalSection(ExtensibilityGlobals) = postSolution 39 | SolutionGuid = {D4BEDF3C-C880-4E96-939D-8D1BCBEF9ED3} 40 | EndGlobalSection 41 | EndGlobal 42 | -------------------------------------------------------------------------------- /vcproject/res/WechatExporter.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/vcproject/res/WechatExporter.ico -------------------------------------------------------------------------------- /vcproject/res/iPhone.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/vcproject/res/iPhone.ico -------------------------------------------------------------------------------- /vcproject/res/iTunes.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/vcproject/res/iTunes.ico -------------------------------------------------------------------------------- /vcproject/res/idr_main.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/vcproject/res/idr_main.ico -------------------------------------------------------------------------------- /vcproject/res/toolbar.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueMatthew/WechatExporter/8f8ce412fc57c4c792cb519e9d1fb980e0f58cfd/vcproject/res/toolbar.bmp -------------------------------------------------------------------------------- /vcproject/stdafx.cpp: -------------------------------------------------------------------------------- 1 | // stdafx.cpp : source file that includes just the standard includes 2 | // WechatExporter.pch will be the pre-compiled header 3 | // stdafx.obj will contain the pre-compiled type information 4 | 5 | #include "stdafx.h" 6 | -------------------------------------------------------------------------------- /vcproject/stdafx.h: -------------------------------------------------------------------------------- 1 | // stdafx.h : include file for standard system include files, 2 | // or project specific include files that are used frequently, but 3 | // are changed infrequently 4 | // 5 | 6 | #pragma once 7 | 8 | // Change these values to use different versions 9 | #define WINVER 0x0601 // Windows 7 10 | #define _WIN32_WINNT 0x0601 11 | #define _WIN32_IE 0x0700 12 | #define _RICHEDIT_VER 0x0500 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | extern CAppModule _Module; 19 | 20 | #include 21 | #include 22 | 23 | #if defined _M_IX86 24 | #pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'\"") 25 | #elif defined _M_IA64 26 | #pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='ia64' publicKeyToken='6595b64144ccf1df' language='*'\"") 27 | #elif defined _M_X64 28 | #pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='amd64' publicKeyToken='6595b64144ccf1df' language='*'\"") 29 | #else 30 | #pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") 31 | #endif 32 | --------------------------------------------------------------------------------