A. 怎樣看出emule下載的源
eMule的官方首頁上寫著:2002年05月13日 一個叫做 Merkur 的人,他不滿意原始eDonkey2000客戶端並且堅信他能夠做的更好,所以他開始製作。他聚集了其它開發人員在他的周圍,並且eMule工程就此誕生。
eMule是一個典型的MFC程序,它的圖形界面等,已經和MFC緊緊融合到了一起。因此通常情況下它只能在windows平台下運行。有一些其它的工程,如aMule等,把它進行了移植,因此跨平台的功能要強些。
其實還有另外一個叫做xMule的工程,不過現在已經人氣快不行了。在aMule的主頁上可以看到eMule移植到linux平台下的一些歷史,最早是有個叫做lMule的工程,他使用wxwidgets來進行eMule的跨平台的移植,這個工程2003年就不再更新了,後來轉變成為xMule工程,它一度是linux平台下eMule的事實上的替代品。但是他們的程序員之間由於理念不同,發生了內訌,導致aMule分裂出來,他們後來矛盾嚴重的時候曾經一度從理念問題上升到互相對對方進行人身攻擊,並且曾經對對方的網站發動過DDos。後來aMule和xMule就是兩個完全不同的工程,xMule現在只有HopeSeekr一個人在維護,基本上也沒有什麼更新了。這一點不僅讓人感慨。今年寒假的時候我曾經和HopeSeekr進行過一些交流,感覺他非常自信,經常拿著aMule的一部分代碼來給我看,說你看看他們的代碼這么這么寫,這簡直就是一陀xx嘛,這種代碼在某些情況下肯定會Crash掉嘛,相反,你看看我們xMule的代碼,這里是這樣這樣,肯定就不會有這種問題了。
eMule從0.42版開始支持Kad技術,這是一個非常重要的里程碑。Kad是一種DHT的協議,它可以使節點之間互相保留一些其它節點的聯系信息,並且利用這樣一個「關系網」尋找到整個網路中的任何一個節點以及上面的資源,整個過程不需要任何中心伺服器。因此向當年搞napster那樣直接端掉中心伺服器就搞跨napster網路一樣來對付eMule的Kad網就毫無作用了。0.42版是2004年2月27日放出的,比eDonkey2000的OverNet晚了將近一年,但是它的Kad網路的規模卻在迅速擴大。Overnet和eMule中的Kad使用的都是Kademlia結構,但是具體的消息報文的格式有區別,因此兩個DHT網路並不能互相兼容。OverNet直到現在,用戶也仍然維持在十萬左右,這是比較近的一篇文章寫的。但是eMule的Kad網的規模有多大,目前卻沒有一個很准確的說法。由此也可以看出開源軟體的力量。目前aMule的Kad網和eMule的Kad網是兼容的,xMule中還沒有Kad的支持。Kad協議的原始paper可以在我們實驗室的機器上下到:
http://bigpc.net.pku.e.cn:8080/paper/new/by%20conference/IPTPS/IPTPS02/Kademlia%20A%20Peer-to-Peer%20Information%20System%20Based%20on%20the%20XOR%20Metric%28IPTPS02%29.pdf
eMule的代碼結構非常合理。雖然代碼量比較大,但是各個功能模塊之間的劃分都很合理。從它的工程文件裡面就可以看出這一點。eMule把表示功能的代碼文件和表示界面的代碼文件分開了,Source Files和Header Files是實現功能的代碼的源文件和頭文件,而Interface Source和Interface Header是實現圖形界面的源文件和頭文件。由於eMule的代碼量太大,本系列將跳過圖形界面的實現,著重分析eMule的功能實現部分的代碼,而且功能實現方面也主要挑主要的部分分析。本節將從emule.cpp開始分析,引出eMule中使用到的幾個主要的功能實現的類,並大體描述它們的作用。最後介紹一下如何在VS2003下編譯eMule。emule中還有不少模塊實現的功能挺有用,我說的是在其它的程序里很有用,可以考慮在其它程序中進行復用。
emule.cpp為類CemuleApp的實現。因此在運行時,首先會運行InitInstance進行一些初始化的工作。從這個函數裡面我們也可以第一次看出那些即將在整個程序中發揮作用的類了。
最開始的時候是計算出程序常用的一些目錄,如配置文件,日誌文件等。接下來是ProcessCommandline,它的作用有兩方面,第一是確認該eMule的運行方式,即命令行後面有沒有參數,第二是確認目前eMule是不是只有一個實例在運行。在一般的情況下,雙擊eMule可執行文件是不會帶參數的。但是通過點擊鏈接或者打開關聯文件的方式打開eMule則相當於帶參數運行eMule。通過在注冊表裡添加一些項目可以讓一個程序和某種鏈接或者某個後綴的文件產生關聯。具體辦法可以參見OtherFunctions.cpp中的Ask4RegFix,BackupReg,RevertReg三個函數的功能。ProcessCommandline中通過創建帶有名稱的互斥信號量來確認是否有其它的eMule實例在運行。對於一個確定的名稱,CreateMutex只能創建一個互斥信號量。因此通過該信號量是否創建成功就可以知道是否有其它eMule實例運行。如果有的話,而且又是帶參數的那種模式,那麼直接把這個參數使用Windows的消息機制發給那個窗口即可,接下來的代碼無非就是如何找到另外一個叫"eMule"的傢伙以及給它發個什麼消息。pstrPendingLink是一個全局變數,表示將要被處理的命令行參數。它將會在初始化完成後一段時間後被處理。
下面兩個比較重要的類是CPreferences和CStatistics。前者掌握著程序的大部分配置數據,後者則進行各種統計。它們的特點都是有很多的成員變數,而且還是靜態的,這種方式可以保證它們的唯一性,而且把這些變數統一到一個類管理。但是實際上並不需要了解每個變數的含義。thePrefs和theStats是這兩個類的唯一的實例。
在處理完其它一些事情,包括創建圖形界面對象CemuleDlg後,接下來可以看到一排一排的創建新對象的語句。這些類將會實現eMule程序運行的主要功能,後面的系列將詳細分析。
最後描述一下如何在VS2003里編譯eMule,由於eMule中用到了一些其它的庫,因此官方在提供eMule的源代碼下載的同時如果也提供這些庫的下載會使源碼包變得很大。因此eMule選擇了讓開發者去那些庫的官方網站下載它們的方式。一般來說,編譯這種工程文件,很重要的地方是要保持各個庫和主程序編譯參數的一致性。這些編譯參數中,最主要的參數有三個,字元集(多位元組/Unicode),調試/發行,單線程/多線程,這樣排列組合一下就有八個版本了。因此編譯的時候如果不注意,就會出現和程序設計無關的錯誤。
eMule0.47a解壓後自帶id3lib庫,還需要下載以下的庫:
zlib:
http://www.gzip.org/zlib/
ResizableLib:
http://sourceforge.net/projects/resizablelib/
Crypto++:
http://www.eskimo.com/~weidai/cryptlib.html
pnglib:
http://www.libpng.org/pub/png/libpng.html
下載它們,解壓它們,編譯它們。編譯的時候注意要和eMule的工程的參數一致:字元集為Unicode,支持多線程安全,調試版或者是發行版可以根據需要選擇,但是也要和eMule的工程參數一致。這些庫的解壓包里通常都能找到VC的工程文件,但是版本低一些,直接轉化就可以了。另外建議編譯這些庫的時候都選擇生成靜態庫,不要生成動態的庫,這樣最後生成的可執行文件就可以自己運行了。編譯完這些庫,包括從其它地方下載的和eMule自帶的id3lib庫和CxImage庫後,就可以開始編譯emule了。而編譯emule也無非就是注意讓它能夠在編譯的時候找到所有的頭文件,以及在鏈接的時候能夠找到所有的庫。在鏈接的時候能夠找到所有的庫可以通過修改工程文件裡面的屬性 ->配置屬性 ->鏈接器 ->輸入->附加依賴項來完成。但是能夠找到所有的頭文件反而需要一些技巧了。由於emule的代碼中對於這些庫的頭文件的包含,在一定程度上限定了那些庫的路徑和emule的工程的路徑的相對位置,因此需要把那些解壓過的庫的目錄移到一些合適的地方,有時還需要給這些目錄改個名稱。
eMule中要讀取的配置文件數量較多,每種配置文件都是自己定義的格式,為了方便讀取和存儲這些文件,eMule中有一個很重要的基礎設施類來復制這些文件操作,它能夠很方便得處理一些常用數據類型的讀寫,並且帶有一定的安全保護機制。這項基礎設施在SafeFile.cpp和SafeFile.h中實現。在kademlia\io目錄下有這項功能的另外一項實現。它們實現的功能基本上相似,但是kademlia\io目錄下的版本實現的時候多了一個以Tag作為單位進行讀寫的功能。這些實現中和另外一項基礎設施,那就是字元串轉化密切相關。StringConversion.cpp和StringConversion.h是eMule中專門復制各類字元串轉化的基礎設施,什麼Unicode啊,多位元組流啊,或者是UTF-8之類的,在這里轉化全部都不是問題。關於字元串轉化,個人推薦盡量使用Unicode的寬字元,這樣可以最大程度得避免亂碼。
SafeFile.cpp或者kademlia\io目錄下的實現都有這樣的特點,那就是把數據操作的行為和數據操作的對象分割開來。它們都定義了一個抽象的數據操作的基類(在SafeFile.cpp中是CFileDataIO,在kademlia目錄下是DataIO.cpp實現的Kademlia::CDataIO),這個類中只負責實現在邏輯上操作一項數據的行為,例如,要讀取出一個32位的整型,那麼就是讀出四個位元組到一個整型數值的地址中,要讀取或者寫入其它類型的數據用的是類似的方法。但是這個類把物理上進行數據操作的方法全部都聲明為純虛函數,即讀出多少個位元組,寫入多少個位元組這樣的。有了這樣一個基類,就可以非常方便得在它上面進行重載,把這些純虛函數定義為向某塊內存中進行讀寫的操作,就能很方便得將較為復雜的數據序列化到一塊連續的內存,而如果這些純虛函數是向文件讀寫的操作,那麼自然就可以很方便得用來讀寫各種格式比較奇怪的自己定義的配置文件了。
這些類要讀取的數據對象通常有這些,各種整型,字元串,以及Tag類型。整型讀寫起來比較簡單,從1個位元組的,2個位元組的到4個,8個或者16個位元組類型的數據讀寫方法都比較類似。這里要稍微提一下16個位元組的那種,16個位元組是128位,是eMule中的Kad網的隨機生成的ID的長度,也是eMule中常用的MD4的hash演算法生成的結果的長度。通常用來直接存取整個的這樣的一個ID。在kademlia\utils目錄下的UInt128.cpp實現了一個表示128位的整數的類,功能十分完善,可以進行一些算術操作,並且可以進行比較,這樣為它以後作為key出現在hash表中打下了基礎。仔細學習UInt128.cpp中的代碼實現可以學到很多在編寫這種自定義的數據對象類型時應該注意的問題。
數據操作中另外一項很重要的操作是字元串,總的原則是先寫一個長度,再寫內容。但是到具體的操作的時候就需要注意這些細節了,如長度是寫4個位元組還是兩個位元組,字元串的內容要不要用UTF-8進行編碼。這些操作就需要和StringConversion.cpp緊密合作了。其實後者的字元串轉化函數很多也是調用ATL的相關函數,只是在外面再包上一層MFC的CString。
在kademlia\io\DataIO.cpp中實現的CDataIO中,還另外實現了按照Tag進行讀寫的功能。這在網路上交換共享文件的元信息非常重要,通常一個文件的元信息就可以分解成很多的Tag,如"文件名=xxx","文件長度=xxx"等等。也就是說,一個Tag就是表示某項屬性等於某個值這樣一個事實。在Opcodes.h這個文件中定義了很多的代碼,其中就有很多常見的Tag的屬性名稱。CDataIO類中存儲Tag的屬性名都是先存一個位元組的類型,再存名稱,最後按照類型存值。
eMule中的這幾項基礎設施都是編寫得比較好的,可以很方便得拿出來復用。像字元串編碼的處理和具有一定數據結構的文件IO操作在很多地方都會很有用。eMule中這些類的實現基本上復制到其它的工程文件中只要稍微修改一下很快就能使用。以後我們還將看到eMule中很多其它很有用的基礎設施。
emule作為一個文件共享方面的程序,首先要對自己共享的所有的文件的信息都十分清楚,類CKnownFileList的作用就是這樣的,它在emule.cpp中隨著cmuleapp類創建的時候被創建。
CKnownFileList類使用了MFC的CMap類來維護內部的hash表,這也可以看出emule和MFC的關系確實非常緊密。這里如果用STL的map其實也是可以的。它內部維護了一個已知的文件的列表和取消了的文件列表。這些hash表的關鍵字都是文件的hash值。這樣能夠判斷出文件名不同而內容相同的文件,而一般要讓不同內容的文件有相同的hash值是非常困難的,這也是hash函數它設計的初衷。因此除非是碰上王小雲教授這樣的牛人,我們基本上可以認為,兩個文件hash值相同就代表了它們內容相同。再來看CKnownFileList.cpp,這個文件其實並不長,因為管理一個列表確實不需要太多種類的操作,如果對於每個具體的文件有一個很強大的類來處理它的話。而這里確實有,它就是CKnownFile。有了這么一個類,我們就可以看到,CKnownFileList類所需要做的工作就是能夠根據一些信息查找到對應的CKnownFile類,能夠復制其它的列表中的信息,能夠把所有的這些信息存成文件,然後下次emule運行的時候能夠把這些信息快速恢復出來,最重要的是能夠在完成以上工作的情況下不造成內存泄漏。
CKnownFile類就是一個專門關注某個特定文件的信息的類,它仍然有其基類CAbstractFile。但是它和CAbstractFile類的主要區別就是CAbstractFile類只有基本的信息存取的功能,而CKnownFile能夠主動的生成這些信息,例如,給一個文件的路徑給CKnownFile,它能夠主動地去獲取和這個文件有關的一切信息,並且把它保存在自己的成員變數里(CreateFromFile)。CKnownFile.cpp文件看上去比較長,是因為它做的工作比較多,現在版本的emule中,除了對某個文件進行全文hash以外,還採用了BT的方式,進行分塊hash,這樣在傳輸文件的時候,即使發生出錯的情況,也可以不必重傳整個文件,而只是重傳有錯誤的那塊,這種機制叫做高級智能損壞處理(AICH,Advanced Intelligent Corruption Handling),這個機制以後再繼續分析。
CKnownFile把讀到的文件信息都保存成一個一個的Tag。它在運行中會盡量得獲取更多的文件信息,例如,對於媒體類型的文件,它能夠調用id3lib庫來獲取諸如作者,唱片發行年代,風格等tag信息。如果是視頻媒體文件,它還會去抓圖(功能實現:CFrameGrabThread)。
CKnownFile還能夠隨時掌握目前該文件的下載情況(內部有個CUpDownClient的列表),當然,還會根據要求序列化和反序列化自己,LoadFromFile和WriteToFile都以CFileDataIO為參數,這樣方便CKnownFileList保存和讀取它的列表中的所有文件的信息。
B. 如何用cemu玩wii和ngc游戲
n多的人喜歡使用電腦玩掌機或者是其他的平台的游戲,其中題目的則是目前最為流行的模擬器,由於模擬器是國外開發的,因此全都是英文的,很多人並不懂得如何運行游戲的。在這里就簡單介紹一下它運行游戲的方法。
方法/步驟:
1/根據模擬器的要求,必須電腦系統是64位的,最低內存低於4g,並且需要c++2015.實際上使用8g的電腦,玩起來還是很吃力的,因此這里認為使用8g為起點最好。並且模擬器支持最好的n卡。自己的電腦的配置可以,點擊開始運行,在運行輸入dxdiag回車就可以看到了。如圖三圖四查看電腦的配置。2、接著進入控制面板,看到程序這里,如圖三是否安裝了c++2015。沒有安裝的則需要進行安裝。3、把模擬器進行解壓,解壓到得到圖二的文件。4、點擊解壓後的藍色文件,啟動軟體後點擊option,選擇菜單上面的input即輸入設置。5、在圖一的位置選擇一個手柄的類型,這里選擇class,因為選擇其他的無法輸入按鍵,如果你選擇的無法輸入按鍵,(例如輸入A上面無法顯示則這個用不了)則選擇可以輸入的。不要選擇remot這個,能夠輸入數字的選擇一個即可。實在不懂的,可以對照著圖二進行設置,你想要設置那個就根據實際的手柄進行設置就可以了。6、當然輸入按鍵後,直接點擊save保存是用不了的,先在圖二的位置輸入一個保存的名字,例如字母s,然後再按s就可以保存設置的按鈕了。模擬器設置好了按鍵後點擊x即可退出,設置一次就會永久生效的。7、然後就去解壓游戲文件了,如圖一你右鍵任何一個解壓文件都是可以的,只要文件是完整的都可以進行解壓。解壓後得到的是一個wud文件,全部都是24g左右。因而游戲基本上都是壓縮包形式的,當然還有其他的游戲格式。8、然後回到模擬器所在文件,打開裡面keys.txt文件,然後輸入圖二的第一行密鑰,這個可以在網上輸入,你也可以手動打入,因為這里不給發因此自己手打一下吧。裡面的內容最好不要刪除任何一行。除了你自己添加的外。如果不填入密鑰則會顯示圖三的提示。9、接著再次輸入游戲的密鑰,不同的游戲的密鑰key不同的,這里最重要的是井號前面的一串密鑰,#後面的可以隨便填入。10、然後選擇wud格式的游戲,讀取游戲後按a就可以選擇游戲了。當然不同的游戲按鍵不同。
C. 怎麼通過cemu查看title key
信手拈來:
Map<String, Object> currMap = new HashMap<String, Object>();
/////////////////////////////////////////////////////////////
for(String string :currMap.keySet())
{
if(string.equals("title"))
{
System.out.println(currMap.get(string));
}
}
/////////////////////////////////////////////////////////////
for(Entry<String, Object> entry:currMap.entrySet())
{
if(entry.getKey().equals("title"))
{
System.out.println(entry.getValue());
}
}
////////////////////////////////////////////////////////////
Iterator<String> iterator = currMap.keySet().iterator();
while(iterator.hasNext())
{
String string = iterator.next();
if (string.equals("title"))
{
System.out.println(currMap.get(string));
}
}