Page 1 of 1

Download Master Script Host

Posted: 01 Apr 2008, 21:54 Tue
by mymigor
Плугин http://mymrigor.narod.ru/DMScriptHost.rar
Исходник http://mymrigor.narod.ru/DMScriptHost_src.rar

Описание плагина.
Позволяет использовать скрипты на скриптовых языках (JScript, VBScript и др.) в DM

Требования к скрипту.
В скрипте должна присутствовать функция EventRaised с двумя параметрами.
Пример на JScript
-------------------------------
function EventRaised(eType, eData)
{
// бла-бла-бла
}
-------------------------------
Возвращаемое значение функции игнорируется.
Все что написано вне функции, будет выполнено при загрузке скрипта.
Также "вне" могут быть описаны глобальные переменные.

В функцию пересылаются ВСЕ сообщения из DM. Кроме этого посылаются 4 дополнительных
('PluginInit', 'auto') - инициализация из DM
('PluginInit', 'reload') - инициализация при перезагрузке скрипта (Reload) через интерфейс плагина
('BeforeUnload', 'auto') - финализация из DM
('BeforeUnload', 'reload') - финализация при перезагрузке скрипта через интерфейс плагина

Из скрипта может быть вызвана функция DoAction следующим образом:
var WshShell = new ActiveXObject("WScript.Shell");
var res = DM.DoAction('GetTempDir', '');
WshShell.Popup(res, 100);

Скрипт может находится в 4 состояниях
1. работает - значит работает

2. незапущен - произошла ошибка при загрузке скрипта по следующим причинам
нет файла скрипта
неверно указан язык скрипта
ошибка компиляции (неверен синтаксис скрипта)
ошибка выполнения (вне функции EventRaised)
отсутствие ф-ии EventRaised, или неверное количество её параметров
причину ошибки можно посмотреть ч/з интерфейс плагина кнопкой Error

3. остановлен - ч/з интерфейс плагина кнопкой Stop
в скрипт перестают приходить сообщения от DM, за исключением PluginInit и BeforeUnload

4. останов по ошибкам - автоматическая остановка при достижении заданного количества ошибок времени выполнения(по дефолту 25 штук)
в скрипт перестают приходить сообщения от DM, за исключением PluginInit и BeforeUnload

Критикуя, предлагайте.

--
ЗЫЖ от 2.4.08. Перевыложил "Reorder Download", исправив одну ошибку. Здесь искали вроде.

Posted: 01 May 2008, 22:13 Thu
by Kela
Отличнейший плагин! Молодец!

Стояла задача: есть 50+ ссылок на html-страницы, на каждой из которой имеется около десятка ссылок на интересующие файлы... Нужно было скачать все эти файлы.

С помощью этого плагина все решается в 30 строк кода (с пробелами :) ): скрипт "бегает" по html-страницам, переберает все линки на предмет соответствия заданному формату, и если все ОК - добавляет закачку.

Еще раз спасибо!

P.s. Если кого будет интересовать - смогу выложить код скрипта... Подогнать под свои нужды не составит труда...

Posted: 02 May 2008, 16:36 Fri
by x2088
Ну так выкладывайте!

Posted: 02 May 2008, 18:12 Fri
by Kela
Держите... Описание и коменты - в тексте. Надеюсь, будет кому-то полезно ;)

Code: Select all

/**************************************************************************
*
*           Это скрипт для плагина Download Master Script Host
*      (http://downloadmaster.net/forum/viewtopic.php?p=25002#25002)
*
* Скрипт можно использовать как угодно: распространять, менять для своих нужд и т.п.
* Скрипт - это только пример, обильно снабженный коментариями. Если вы знаете лучший метод
*           или у вас свой полезный скрипт - поделитесь им с народом в теме плагина.
*
* Что делает скрипт: скрипт добавляет в закачку ссылки, которые находятся на нескольких
*                    однотипных страницах (смотрите код и комментарии).
*                    Ссылки можно предварительно проверить и отобрать только нужные,
*                    задав соответствуещее условие. Пример легко модифицировать для своих нужд.
*
* Скрипт работает не очень быстро и это понятно: для каждой страницы мы создаем
*                 объект Internet Explorer и загружаем в него страницу. Но мне спешить было некуда. :-)
*
* ВНИМАНИЕ!!! Скрипт не начинает работу автоматически при старте DM. Для начала его работы нужно
*             остановить скрипт в плагине и нажать кнопку Reload. Мне так было нужно.
*             Если вас это не страивает, используйте документацию к плагину и измените это условие:
*             if (eType == 'PluginInit' && eData == 'reload')
*
**************************************************************************/


function EventRaised(eType, eData)
{
	if (eType == 'PluginInit' && eData == 'reload')
	{
		// Здесь укажите нужные параметры счетчика
		for (i = 0; i < 10; i++)
		{
			// Создаем объект Internet Explorer, чтобы через него получить доступ к DOM html-страницы
			// Если вы знаете другой метод - пожалуйста. Но этот метод проверен в работе.
			
			var IE = new ActiveXObject("InternetExplorer.Application");

			// Здесь укажите правильный адрес страницы. Это только пример
			// В примере будет открыто 10 страниц типа:
			// http://адрес.сайта.com/index.php?page=1
			
			IE.Navigate("http://адрес.сайта.com/index.php?page=" + i);

			// Ждем пока страница загрузится...
			// Метод тупой, но пауза не работает почему-то
			while (IE.ReadyState != 4);

			// После загрузки документа мы имеем полный доступ к его DOM
			// IE.Document - объект-документ
			// Полную документацию смотрите в MSDN
			// Объект InternetExplorer.Application: http://msdn.microsoft.com/en-us/library/aa752084(VS.85).aspx
			// Объект Document: http://msdn.microsoft.com/en-us/library/aa752574(VS.85).aspx
			// И т.д.
			
			// В примере нас будут интересовать ссылки: IE.Document.links
			// Если вам нужны картинки - используйте IE.Document.images и т.д.
			// Вообщем, смотрите документацию по ссылкам выше - там все есть
			
			var links = IE.Document.links;

			// Обходим все ссылки в документе...
			for (j = 0; j < links.length; j++)
			{
				// Получаем, куда указует ссылка
				var href = links.item(j).href.toString();
				
				// ... и ищем те, которые нас удовлетворяют. Вы можете здесь написать абсолютно любое условие.
				// В примере мы будем качать только ссылки, которые начинаются на 'http://адрес.сайта.com/zip/'
				if (href.indexOf('http://адрес.сайта.com/zip/') == 0)
				{
					// Самое время нагрузить работой DM! :-)
					// Добавляем закачку!
					
					var res = DM.DoAction('AddingURL', '<url>' + href + '</url> <hidden>1</hidden>');
					
					// ВНИМАНИЕ!!!
					// Присваивать результат работы DoAction какой-то переменной ОБЯЗАТЕЛЬНО!!! Даже если вам результат и не нужен...
					// Я не знаю в чем причина, но без этого закачка не добавляется, а плагин говорит об ошибке!
					// Не повторяйте мою ошибку, я пока понял в чем дело - много сигарет выкурил :-) 
				}
			}
			
			// Закрываем IE.
			// Конечно, можно было создать IE один раз, а потом просто менять ссылки, но так работать не хочет - после первого прохода
			// IE не меняет значение свойства ReadyState, которое мы используем, чтобы узнать, что документ полностью загружен.
			IE.Quit();
			IE = null;
		}
	}
}

Posted: 27 Jun 2008, 0:05 Fri
by ponand
Помагите чайнику скрипт нехочет работать.

Posted: 28 Jun 2008, 12:34 Sat
by ponand
Мне кто нибуть поможет?

Posted: 28 Nov 2008, 19:23 Fri
by ALEKCEN
Я как понял этот скрипт написан в формате JS ...это так ? (тоесть надо сохранить и переименовать в .js)

Ах да,что ещё хотел спросить...я правельно понял,этот плуг,добавляет возможность использования .js скриптов в ДМ ?
если да,то единственно,что хренова - это условие,что строчка (написаная в посте) должна присутствовать в скрипте...

Posted: 02 Dec 2008, 10:12 Tue
by Fktrc
Я, конечно, все понимаю, но этот пример неудачен в том плане, что в данном конкретном случае плагин ни разу не обязателен. Достаточно запуска такой команды:

Code: Select all

"C:\Program Files\Download Master\dmaster.exe" http://s44.radikal.ru/i106/0811/11/f36e9b4dbe49.gif http://www.ya.ru/ "description=превед, кросафчег" filename=картинко.гиф savepath=c:\ hidden=1 start=0
Приведите, плз, кто-нить пример задачи, которая на WSH без данного плагина не решается.

Re: Download Master Script Host

Posted: 21 Nov 2010, 11:07 Sun
by Alex Qwerty
Костыль для автоматической перекодировки имен-путей ANSI>UTF8 и доменов в punycode.

Известные баги:
теряются зеркала.

Code: Select all

// version: 2
// Добавил перекодировку национальных доменов в punycode при добавлении закачки.


//http://upload.wikimedia.org/wikipedia/ru/8/8c/Берите_пример_со_знатного_коноплевода_Александра_Хрипунова_(плакат).jpg
//http://кудяплики.рф/uploads/images/00/00/02/2010/12/02/dc265c.jpg

// "var punycode = new function Punycode() {" не работает: "Имя задано неоднозначно"!

//http://stackoverflow.com/questions/183485/can-anyone-recommend-a-good-free-javascript-for-punycode-to-unicode-conversion
//Javascript Punycode converter derived from example in RFC3492.
//This implementation is created by some@domain.name and released into public domain
var punycode = new function() {
    // This object converts to and from puny-code used in IDN
    //
    // punycode.ToASCII ( domain )
    //
    // Returns a puny coded representation of "domain".
    // It only converts the part of the domain name that
    // has non ASCII characters. I.e. it dosent matter if
    // you call it with a domain that already is in ASCII.
    //
    // punycode.ToUnicode (domain)
    //
    // Converts a puny-coded domain name to unicode.
    // It only converts the puny-coded parts of the domain name.
    // I.e. it dosent matter if you call it on a string
    // that already has been converted to unicode.
    //
    //
    this.utf16 = {
        // The utf16-class is necessary to convert from javascripts internal character representation to unicode and back.
        decode:function(input){
            var output = [], i=0, len=input.length,value,extra;
            while (i < len) {
                value = input.charCodeAt(i++);
                if ((value & 0xF800) === 0xD800) {
                    extra = input.charCodeAt(i++);
                    if ( ((value & 0xFC00) !== 0xD800) || ((extra & 0xFC00) !== 0xDC00) ) {
                        throw new RangeError("UTF-16(decode): Illegal UTF-16 sequence");
                    }
                    value = ((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000;
                }
                output.push(value);
            }
            return output;
        },
        encode:function(input){
            var output = [], i=0, len=input.length,value;
            while (i < len) {
                value = input[i++];
                if ( (value & 0xF800) === 0xD800 ) {
                    throw new RangeError("UTF-16(encode): Illegal UTF-16 value");
                }
                if (value > 0xFFFF) {
                    value -= 0x10000;
                    output.push(String.fromCharCode(((value >>>10) & 0x3FF) | 0xD800));
                    value = 0xDC00 | (value & 0x3FF);
                }
                output.push(String.fromCharCode(value));
            }
            return output.join("");
        }
    }

    //Default parameters
    var initial_n = 0x80;
    var initial_bias = 72;
    var delimiter = "\x2D";
    var base = 36;
    var damp = 700;
    var tmin=1;
    var tmax=26;
    var skew=38;
    var maxint = 0x7FFFFFFF;

    // decode_digit(cp) returns the numeric value of a basic code
    // point (for use in representing integers) in the range 0 to
    // base-1, or base if cp is does not represent a value.

    function decode_digit(cp) {
        return cp - 48 < 10 ? cp - 22 : cp - 65 < 26 ? cp - 65 : cp - 97 < 26 ? cp - 97 : base;
    }

    // encode_digit(d,flag) returns the basic code point whose value
    // (when used for representing integers) is d, which needs to be in
    // the range 0 to base-1. The lowercase form is used unless flag is
    // nonzero, in which case the uppercase form is used. The behavior
    // is undefined if flag is nonzero and digit d has no uppercase form.

    function encode_digit(d, flag) {
        return d + 22 + 75 * (d < 26) - ((flag != 0) << 5);
        //  0..25 map to ASCII a..z or A..Z
        // 26..35 map to ASCII 0..9
    }
    //** Bias adaptation function **
    function adapt(delta, numpoints, firsttime ) {
        var k;
        delta = firsttime ? Math.floor(delta / damp) : (delta >> 1);
        delta += Math.floor(delta / numpoints);

        for (k = 0; delta > (((base - tmin) * tmax) >> 1); k += base) {
                delta = Math.floor(delta / ( base - tmin ));
        }
        return Math.floor(k + (base - tmin + 1) * delta / (delta + skew));
    }

    // encode_basic(bcp,flag) forces a basic code point to lowercase if flag is zero,
    // uppercase if flag is nonzero, and returns the resulting code point.
    // The code point is unchanged if it is caseless.
    // The behavior is undefined if bcp is not a basic code point.

    function encode_basic(bcp, flag) {
        bcp -= (bcp - 97 < 26) << 5;
        return bcp + ((!flag && (bcp - 65 < 26)) << 5);
    }

    // Main decode
    this.decode=function(input,preserveCase) {
        // Dont use utf16
        var output=[];
        var case_flags=[];
        var input_length = input.length;

        var n, out, i, bias, basic, j, ic, oldi, w, k, digit, t, len;

        // Initialize the state:

        n = initial_n;
        i = 0;
        bias = initial_bias;

        // Handle the basic code points: Let basic be the number of input code
        // points before the last delimiter, or 0 if there is none, then
        // copy the first basic code points to the output.

        basic = input.lastIndexOf(delimiter);
        if (basic < 0) basic = 0;

        for (j = 0; j < basic; ++j) {
            if(preserveCase) case_flags[output.length] = ( input.charCodeAt(j) -65 < 26);
            if ( input.charCodeAt(j) >= 0x80) {
                throw new RangeError("Illegal input >= 0x80");
            }
            output.push( input.charCodeAt(j) );
        }

        // Main decoding loop: Start just after the last delimiter if any
        // basic code points were copied; start at the beginning otherwise.

        for (ic = basic > 0 ? basic + 1 : 0; ic < input_length; ) {

            // ic is the index of the next character to be consumed,

            // Decode a generalized variable-length integer into delta,
            // which gets added to i. The overflow checking is easier
            // if we increase i as we go, then subtract off its starting
            // value at the end to obtain delta.
            for (oldi = i, w = 1, k = base; ; k += base) {
                    if (ic >= input_length) {
                        throw RangeError ("punycode_bad_input(1)");
                    }
                    digit = decode_digit(input.charCodeAt(ic++));

                    if (digit >= base) {
                        throw RangeError("punycode_bad_input(2)");
                    }
                    if (digit > Math.floor((maxint - i) / w)) {
                        throw RangeError ("punycode_overflow(1)");
                    }
                    i += digit * w;
                    t = k <= bias ? tmin : k >= bias + tmax ? tmax : k - bias;
                    if (digit < t) { break; }
                    if (w > Math.floor(maxint / (base - t))) {
                        throw RangeError("punycode_overflow(2)");
                    }
                    w *= (base - t);
            }

            out = output.length + 1;
            bias = adapt(i - oldi, out, oldi === 0);

            // i was supposed to wrap around from out to 0,
            // incrementing n each time, so we'll fix that now:
            if ( Math.floor(i / out) > maxint - n) {
                throw RangeError("punycode_overflow(3)");
            }
            n += Math.floor( i / out ) ;
            i %= out;

            // Insert n at position i of the output:
            // Case of last character determines uppercase flag:
            if (preserveCase) { case_flags.splice(i, 0, input.charCodeAt(ic -1) -65 < 26);}

            output.splice(i, 0, n);
            i++;
        }
        if (preserveCase) {
            for (i = 0, len = output.length; i < len; i++) {
                if (case_flags[i]) {
                    output[i] = (String.fromCharCode(output[i]).toUpperCase()).charCodeAt(0);
                }
            }
        }
        return this.utf16.encode(output);
    };

    //** Main encode function **

    this.encode = function (input,preserveCase) {
        //** Bias adaptation function **

        var n, delta, h, b, bias, j, m, q, k, t, ijv, case_flags;

        if (preserveCase) {
            // Preserve case, step1 of 2: Get a list of the unaltered string
            case_flags = this.utf16.decode(input);
        }
        // Converts the input in UTF-16 to Unicode
        input = this.utf16.decode(input.toLowerCase());

        var input_length = input.length; // Cache the length

        if (preserveCase) {
            // Preserve case, step2 of 2: Modify the list to true/false
            for (j=0; j < input_length; j++) {
                case_flags[j] = input[j] != case_flags[j];
            }
        }

        var output=[];


        // Initialize the state:
        n = initial_n;
        delta = 0;
        bias = initial_bias;

        // Handle the basic code points:
        for (j = 0; j < input_length; ++j) {
            if ( input[j] < 0x80) {
                output.push(
                    String.fromCharCode(
                        case_flags ? encode_basic(input[j], case_flags[j]) : input[j]
                    )
                );
            }
        }

        h = b = output.length;

        // h is the number of code points that have been handled, b is the
        // number of basic code points

        if (b > 0) output.push(delimiter);

        // Main encoding loop:
        //
        while (h < input_length) {
            // All non-basic code points < n have been
            // handled already. Find the next larger one:

            for (m = maxint, j = 0; j < input_length; ++j) {
                ijv = input[j];
                if (ijv >= n && ijv < m) m = ijv;
            }

            // Increase delta enough to advance the decoder's
            // <n,i> state to <m,0>, but guard against overflow:

            if (m - n > Math.floor((maxint - delta) / (h + 1))) {
                throw RangeError("punycode_overflow (1)");
            }
            delta += (m - n) * (h + 1);
            n = m;

            for (j = 0; j < input_length; ++j) {
                ijv = input[j];

                if (ijv < n ) {
                    if (++delta > maxint) return Error("punycode_overflow(2)");
                }

                if (ijv == n) {
                    // Represent delta as a generalized variable-length integer:
                    for (q = delta, k = base; ; k += base) {
                        t = k <= bias ? tmin : k >= bias + tmax ? tmax : k - bias;
                        if (q < t) break;
                        output.push( String.fromCharCode(encode_digit(t + (q - t) % (base - t), 0)) );
                        q = Math.floor( (q - t) / (base - t) );
                    }
                    output.push( String.fromCharCode(encode_digit(q, preserveCase && case_flags[j] ? 1:0 )));
                    bias = adapt(delta, h + 1, h == b);
                    delta = 0;
                    ++h;
                }
            }

            ++delta, ++n;
        }
        return output.join("");
    }

    this.ToASCII = function ( domain ) {
        var domain_array = domain.split(".");
        var out = [];
        for (var i=0; i < domain_array.length; ++i) {
            var s = domain_array[i];
            out.push(
                s.match(/[^A-Za-z0-9-]/) ?
                "xn--" + punycode.encode(s) :
                s
            );
        }
        return out.join(".");
    }
    this.ToUnicode = function ( domain ) {
        var domain_array = domain.split(".");
        var out = [];
        for (var i=0; i < domain_array.length; ++i) {
            var s = domain_array[i];
            out.push(
                s.match(/^xn--/) ?
                punycode.decode(s.slice(4)) :
                s
            );
        }
        return out.join(".");
    }
}();


var WshShell = new ActiveXObject("WScript.Shell");

var ansiToUtf8 = {"Ё":"%D0%81","ё":"%D1%91","А":"%D0%90","Б":"%D0%91","В":"%D0%92","Г":"%D0%93","Д":"%D0%94","Е":"%D0%95","Ж":"%D0%96","З":"%D0%97","И":"%D0%98","Й":"%D0%99","К":"%D0%9A","Л":"%D0%9B","М":"%D0%9C","Н":"%D0%9D","О":"%D0%9E","П":"%D0%9F","Р":"%D0%A0","С":"%D0%A1","Т":"%D0%A2","У":"%D0%A3","Ф":"%D0%A4","Х":"%D0%A5","Ц":"%D0%A6","Ч":"%D0%A7","Ш":"%D0%A8","Щ":"%D0%A9","Ъ":"%D0%AA","Ы":"%D0%AB","Ь":"%D0%AC","Э":"%D0%AD","Ю":"%D0%AE","Я":"%D0%AF","а":"%D0%B0","б":"%D0%B1","в":"%D0%B2","г":"%D0%B3","д":"%D0%B4","е":"%D0%B5","ж":"%D0%B6","з":"%D0%B7","и":"%D0%B8","й":"%D0%B9","к":"%D0%BA","л":"%D0%BB","м":"%D0%BC","н":"%D0%BD","о":"%D0%BE","п":"%D0%BF","р":"%D1%80","с":"%D1%81","т":"%D1%82","у":"%D1%83","ф":"%D1%84","х":"%D1%85","ц":"%D1%86","ч":"%D1%87","ш":"%D1%88","щ":"%D1%89","ъ":"%D1%8A","ы":"%D1%8B","ь":"%D1%8C","э":"%D1%8D","ю":"%D1%8E","я":"%D1%8F"};
//1-протокол, логин-пароль; 2 - хост; 3 - остальное.
//               1                                                2              3
var parseUrl = /^((?:https?|ftp):\/\/(?:[\w\.-]+(?::[\w\.-]+)?@)?)([\w\.а-яЁё-]+)((?::\d+)?\/.*)?$/i;
// http://ru.wikipedia.org/wiki/URI#.D0.A0.D0.B0.D0.B7.D0.B1.D0.BE.D1.80_.D1.81.D1.82.D1.80.D1.83.D0.BA.D1.82.D1.83.D1.80.D1.8B_URI

function EventRaised(eType, eData)
{
	var tmp=eData.split(' ');
	var ID=tmp[0];
	var dState=tmp[1];

	if ( !(eType == 'dm_download_added') && !(eType == 'dm_download_state' && dState=='4') ) return;

	var info = DM.DoAction('GetDownloadInfoByID', ID);
	var url = info.match(/<url>(.*?)<\/url>/im)[1];
	var newUrl = url.match(parseUrl);
	newUrl[0] = '';

	// перекодирую в utf-8 русские буквы в пути и имени файла
	if (eType == 'dm_download_state' && dState=='4') {	// dsError = 4
		var descr = info.match(/<description>(.*?)<\/description>/im)[1];
		if (descr.indexOf('[ansiToUtf8]')!=-1) {
			tmp = DM.DoAction('AddStringToLog','<id>'+ID+'</id><type>3</type><logstring>[ansiToUtf8] label exists - aborting</logstring>');
			return;
		}
		newUrl[3] = newUrl[3].replace(/[а-яЁё]/gi, function(i){ return ansiToUtf8[i]; });
		newUrl=newUrl.join('');
		if (newUrl == url) return;
		var newData='<id>'+ID+'</id><mirror1>'+newUrl+'</mirror1><description>'+descr+' [ansiToUtf8]</description>';
		tmp = DM.DoAction('SetDownloadInfoByID',newData);
		tmp = DM.DoAction('AddStringToLog','<id>'+ID+'</id><type>2</type><logstring>[ansiToUtf8] mirror added - restarting</logstring>');
		tmp = DM.DoAction('StartDownloads',ID);
	}
	// перекодирую хост в punycode - для русских доменов
	else if (eType == 'dm_download_added') {
		if (newUrl[2].search(/[^\da-z\.-]/i) == -1) return;
		newUrl[2] = punycode.ToASCII(newUrl[2]);
		newUrl=newUrl.join('');
		if (newUrl == url) return;
		var newData='<id>'+ID+'</id><url>'+newUrl+'</url>';
		tmp = DM.DoAction('SetDownloadInfoByID',newData);
		tmp = DM.DoAction('AddStringToLog','<id>'+ID+'</id><type>2</type><logstring>[ansiToUtf8] punycode: '+url+'</logstring>');
	}
}

Re: Download Master Script Host

Posted: 23 Nov 2010, 13:37 Tue
by Korney San
Alex Qwerty wrote: Известные баги:
теряются зеркала.
ПМСМ в первую очередь надо использовать поле referer, в таких случая оно обычно пустое, и только потом трогать зеркала.
Да и в зеркалах лучше брать не mirror1, а mirror5 - редко когда столько набирают.

Re: Download Master Script Host

Posted: 23 Nov 2010, 14:09 Tue
by Alex Qwerty
Korney San wrote:ПМСМ в первую очередь надо использовать поле referer
Бэкап основного урла ради сохранности зеркал? Лень возиться.
Да и в зеркалах лучше брать не mirror1, а mirror5 - редко когда столько набирают.
Не важно: DM затрет все предыдущие, а через GetDownloadInfoByID их не отдает (вставь "WshShell.Popup(info);"). Самому выковыривать из default.xml опять-таки лень.

Re: Download Master Script Host

Posted: 03 Apr 2012, 10:40 Tue
by ponand
ссылки битые

Re: Download Master Script Host

Posted: 22 Dec 2013, 10:34 Sun
by todk
http://mymrigor.narod.ru/DMScriptHost.rar
http://mymrigor.narod.ru/DMScriptHost_src.rar

Перезалейте пожалуйста эти файлы или скиньте в личку.

Спасибо

Re: Download Master Script Host

Posted: 01 Jan 2015, 4:36 Thu
by unreal666
перезалейте плиз эти файлы