mldonkey_importer.pl can now import an unlimited number of files
[amule.git] / src / utils / scripts / mldonkey_importer.pl
blob76925883e4e8928f2103adeb08788f104c4fb8e0
1 #!/usr/bin/perl
4 ## This file is part of the aMule Project
5 ##
6 ## Copyright (c) 2004-2011 Angel Vidal ( kry@amule.org )
7 ## Copyright (c) 2003-2011 aMule Team ( admin@amule.org / http://www.amule.org )
8 ##
9 ## This program is free software; you can redistribute it and/or
10 ## modify it under the terms of the GNU General Public License
11 ## as published by the Free Software Foundation; either
12 ## version 2 of the License, or (at your option) any later version.
14 ## This program is distributed in the hope that it will be useful,
15 ## but WITHOUT ANY WARRANTY; without even the implied warranty of
16 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 ## GNU General Public License for more details.
19 ## You should have received a copy of the GNU General Public License
20 ## along with this program; if not, write to the Free Software
21 ## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23 # Gimme a break, is my first perl app... (Kry)
25 use File::Copy;
26 use warnings;
27 use strict;
29 my $exit_with_help;
31 if (!($ARGV[0])) {
32 print "You must specify the mldonkey config folder (usually ~/.mldonkey).\n";
33 $exit_with_help = "true";
36 if (!($ARGV[1])) {
37 print "You must specify the aMule temp folder for output.\n";
38 $exit_with_help = "true";
41 if ($exit_with_help) {
42 die "Usage: importer2.pl mldonkey_config_folder amule_temp_folder.\n";
46 my $input_folder = $ARGV[0];
48 my $output_folder = $ARGV[1];
50 open(TEST,">" . $output_folder . "/test_file") or die "Unable to write to destination folder! Error: $!\n";
51 close(TEST);
52 unlink($output_folder . "/test_file");
54 open(INFO, $input_folder . "/files.ini") or die "Cannot open input file" . $input_folder . "/files.ini for reading: $!"; # Open the file
56 my $line="no";
57 while ($line !~ /^\s*files\s*=\s*\[\s*$/) {
58 $line = <INFO>;
59 if (!($line)) {
60 die $input_folder . "/files.ini seems not to be a mldonkey files.ini\n";
62 chop $line;
65 #We're at the start of the downloading files section.
66 # Read info for each file.
68 my $number = 1;
70 while ($line && ($line !~ /^.*};\].*$/)) {
71 print "Reading info for file $number\n";
72 &read_file_info;
73 print "End reading\n\n";
74 $number++;
77 close(INFO);
79 sub read_file_info {
80 $line = <INFO>;
82 my @md4_list = ();
83 my @gap_list = ();
84 my $file_size = 0;
85 my $file_name = "";
86 my $part_file = "";
87 my $md4_hash = "";
89 my $done = "false";
91 while (($line) && ($line !~ /^\s*}.*/) && ($done ne "true")) {
92 chop $line;
93 if ($line =~ /.*file_network\s*=\s*(.*)$/) {
94 print "Network is $1\n";
95 if ($1 ne "Donkey") {
96 print "Cannot import non-ed2k part file, skipping\n";
97 while (($line) && ($line !~ /^\s*}.*/)) {
98 $line = <INFO>;
99 $done = "true";
103 if ($line =~ /^\s*file_size\s*=\s*(\d+)\s*$/) {
104 $file_size = $1;
105 print "File size: $file_size\n";
107 if ($line =~ /^\s*file_swarmer\s*=\s*\"(.*)\"\s*$/) {
108 $part_file = $1;
109 print "Part file to import: $part_file\n";
111 if ($line =~ /^\s*file_md4\s*=\s*\"?(([A-Z]|[0-9])+)\"?\s*$/) {
112 $md4_hash = $1;
113 print "File hash: $md4_hash\n";
115 if ($line =~ /^\s*file_filename\s*=\s*\"(.*)\"\s*$/) {
116 $file_name = $1;
117 print "File name: $file_name\n";
119 if ($line =~ /^\s*file_md4s\s*=\s*\[\s*$/) {
120 # Read the MD4 list
121 my $result = "";
122 do {
123 my $md4_line = <INFO>;
124 if ($md4_line =~ /^\s*\"?(([A-Z]|[0-9])+)\"?;\]?\s*$/) {
125 push(@md4_list,$1);
126 if ($md4_line =~ /^.*;\].*$/) {
127 $result = "done";
129 } else {
130 print "Malformed md4 hash line $md4_line";
131 @md4_list = ();
132 $result = "error";
134 } while (!($result));
135 if ($result eq "done") {
136 print "MD4 list: @md4_list\n";
142 if ($line =~ /^\s*file_present_chunks\s*=\s*\[\s*$/) {
143 # Read the gaps list
144 my $result = "";
145 my @ml_gaps = ();
146 do {
147 my $gaps_line = <INFO>;
148 if ($gaps_line =~ /^\s*\((\d+),\s*(\d+)\)(;|])\s*$/) {
149 push(@ml_gaps,$1);
150 push(@ml_gaps,$2);
151 if ($gaps_line =~ /^.*\)\].*$/) {
152 $result = "done";
154 } else {
155 print "Malformed gaps line $gaps_line";
156 $result = "error";
158 } while (!($result));
160 if ($result eq "done") {
161 # Process mldonkey gaps to aMule gaps
162 print "ML Gaps list: @ml_gaps\n";
164 @gap_list = &convert_gap_format($file_size,@ml_gaps);
166 print "aMule Gaps list: @gap_list\n";
172 if ($done ne "true") {
173 $line = <INFO>;
177 if ($done eq "true") {
178 print "File import result: false\n";
179 } else {
180 if ($file_name && $file_size && $md4_hash && $part_file) {
181 if (!(@md4_list)) {
182 print "WARNING: File has no md4 hashes list, imported file will have 0 bytes downloaded\n";
185 my $first_free_number = &get_first_free_number;
187 my $met_file = $output_folder . sprintf("/%03d.part.met",$first_free_number);
189 &create_met_file($met_file,$file_name,$file_size,$md4_hash,@md4_list,"---",@gap_list);
191 print "File $met_file imported successfully.\n";
193 my $from = $input_folder . "/" . $part_file;
194 my $destination = $output_folder . sprintf("/%03d.part",$first_free_number);
195 copy($from, $destination) or die "CRITICAL: File $from cannot be copied to $destination. Error: $!\n";
197 } else {
198 print "Not enough info to import file, sorry.\n";
201 $line;
204 sub create_met_file {
206 print "Parameters: @_\n";
208 #Open the new file
209 open(MET," > $_[0]");
210 binmode MET;
212 my $large_file = "";
214 # Met file version (1 byte)
215 if ($_[2] < 4290048000) {
216 # Not large file
217 $large_file = "no";
218 printf MET &byte_string(0xe0);
219 } else {
220 $large_file = "yes";
221 printf MET &byte_string(0xe2);
223 # File modification time. 0 to force aMule rehash. (4 bytes)
224 print MET &int32_string(0);
226 # MD4 hash (16 bytes)
227 print MET &hash_string($_[3]);
229 #Calculate number of MD4 hashes
230 my @md4_hashlist = ();
232 my $i = 4;
234 while ($_[$i] ne "---") {
235 push (@md4_hashlist,$_[$i]);
236 $i++;
239 $i++;
241 my @gaps_list = ();
242 while ($_[$i]) {
243 push(@gaps_list,$_[$i]);
244 $i++;
247 print "Write aMule gap list: @gaps_list\n";
249 my $md4_hashsize = @md4_hashlist;
251 print "MD4 hashlist size $md4_hashsize\n";
253 #Number of MD4 hashes (2 bytes)
254 print MET &int16_string($md4_hashsize);
256 #Write MD4 hashes (16 bytes * number of hashes)
257 my $md4_parthash = "";
258 foreach $md4_parthash (@md4_hashlist) {
259 print MET &hash_string($md4_parthash);
262 #Number of tags (4 bytes)
264 my $tags_number = 2; # Fixed tags (Name + Size)
266 $tags_number = $tags_number + @gaps_list;
268 print MET &int32_string($tags_number);
270 #Name tag (x bytes)
272 print MET &tag_string(2,0,0x01,$_[1]); # Tagtype string, id FT_FILENAME, value
274 #Size tag (x bytes)
276 if ($large_file eq "yes") {
277 print MET &tag_string(0x0b,0,0x02,$_[2]); # Tagtype UINT64, id FT_FILESIZE, value
278 } else {
279 print MET &tag_string(3,0,0x02,$_[2]); # Tagtype UINT32, id FT_FILESIZE, value
282 my $t = 0;
284 my $tag_type;
285 if ($large_file eq "yes") {
286 $tag_type = 0x0b;
287 } else {
288 $tag_type = 0x03;
291 while (@gaps_list[$t*2]) {
292 my $gap_start = @gaps_list[$t*2];
293 my $gap_end = @gaps_list[$t*2+1];
295 print "Gap $t start $gap_start end $gap_end\n";
297 print MET &tag_string($tag_type,1,sprintf("%c%d",0x09,$t),$gap_start);
298 print MET &tag_string($tag_type,1,sprintf("%c%d",0x0a,$t),$gap_end);
300 $t++;
303 close(MET);
306 sub byte_string {
307 sprintf("%c",$_[0]);
310 sub int16_string {
311 &byte_string($_[0] % 256) . &byte_string($_[0] / 256);
314 sub int32_string {
315 &int16_string($_[0] % 65536) . &int16_string($_[0] / 65536);
318 sub int64_string {
319 &int32_string($_[0] % 4294967296) . &int32_string($_[0] / 4294967296);
322 sub hash_string {
323 my $i = 0;
324 my $final_string = "";
325 while ($i < 32) {
326 $final_string = $final_string . &byte_string(hex(substr($_[0],$i,2)));
327 $i += 2;
329 $final_string;
332 sub tag_string {
333 # ONLY STRINGS AND UINT32/64 SUPPORTED
335 my $final_string = "";
337 # Tag type
338 $final_string = $final_string . &byte_string($_[0]);
340 if ($_[1] == 0) {
341 # Byte ID tag
342 $final_string = $final_string . &int16_string(1);
343 $final_string = $final_string . &byte_string($_[2]);
344 } else {
345 # String ID tag
346 $final_string = $final_string . &int16_string(length $_[2]) . $_[2];
349 if ($_[0] == 2) {
350 $final_string = $final_string . &int16_string(length $_[3]) . $_[3];
351 } else {
352 if ($_[0] == 3) {
353 # UINT32
354 $final_string = $final_string . &int32_string($_[3]);
355 } else {
356 if ($_[0] == 0x0b) {
357 # UINT64
358 $final_string = $final_string . &int64_string($_[3]);
362 $final_string;
365 sub convert_gap_format {
366 my $total_size = $_[0];
368 my @converted_gaps = ();
370 my $n = 1;
372 if ($_[1] != 0) {
373 push(@converted_gaps,0);
374 push(@converted_gaps,$_[1]);
377 $n++;
379 while ($_[$n+1]) {
380 push(@converted_gaps,$_[$n]);
381 push(@converted_gaps,$_[$n+1]);
382 $n += 2;
385 if ($_[$n] != $total_size) {
386 push(@converted_gaps,$_[$n]);
387 push(@converted_gaps,$total_size);
390 @converted_gaps;
393 sub get_first_free_number {
394 my $n = 1;
396 while (-f sprintf("$output_folder/%03d.part.met",$n)) {
397 $n++;