diff --git a/DB/.db b/DB/.db new file mode 100644 index 0000000..e69de29 diff --git a/dllsource/compile.bat b/dllsource/compile.bat index bfd0edd..f5dd05f 100644 --- a/dllsource/compile.bat +++ b/dllsource/compile.bat @@ -1,7 +1,12 @@ -@if "%1"=="" GOTO :EOF -@if "%1"=="dllplugin" GOTO :EOF -@echo Compile to %1.dll -@copy dllplugin.c %1.c 1>nul -@..\tcc\tcc -shared %1.c -@del %1.def -@del %1.c \ No newline at end of file +@echo OFF +%~d0 +cd %~dp0 +set FULLPATH=%~dp0 + +if "%1"=="" GOTO :EOF +if "%1"=="dllplugin" GOTO :EOF +echo Compile to %1.dll +copy dllplugin.c %1.c 1>nul +..\tcc\tcc -shared %FULLPATH%%1.c +del %1.def +del %1.c \ No newline at end of file diff --git a/dllsource/compile_all_p.bat b/dllsource/compile_all_p.bat index 6018e7d..44d0866 100644 --- a/dllsource/compile_all_p.bat +++ b/dllsource/compile_all_p.bat @@ -1,6 +1,7 @@ -%~d0 -cd %~p0 +@ECHO OFF +%~d0 +cd %~dp0 -@C:\mbplugin\python\python -c "import os,glob;fl=[os.system(f'compile.bat p_{os.path.splitext(os.path.split(fn)[1])[0]}') for fn in glob.glob('..\\plugin\\*.py') if 'def get_balance(' in open(fn,encoding='utf8').read()]" -move C:\mbplugin\dllsource\*.dll C:\mbplugin\dllplugin +@..\python\python -c "import os,glob;fl=[os.system(f'compile.bat p_{os.path.splitext(os.path.split(fn)[1])[0]}') for fn in glob.glob('..\\plugin\\*.py') if 'def get_balance(' in open(fn,encoding='utf8').read()]" +move ..\dllsource\*.dll ..\dllplugin diff --git a/dllsource/dllplugin.c b/dllsource/dllplugin.c index 2e3c082..9bbd056 100644 --- a/dllsource/dllplugin.c +++ b/dllsource/dllplugin.c @@ -11,9 +11,17 @@ #define MAXBUF 10000 #define BUFFSZ 4096 +// from https://stackoverflow.com/questions/8487986/file-macro-shows-full-path +#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__) + //from https://stackoverflow.com/questions/35969730/how-to-read-output-from-cmd-exe-using-createprocess-and-createpipe int callInterpreter(char *request, char *dllname, char *response, int responseSize) { + char dllpath[MAX_PATH] = {0}; + char mbpluginpath[MAX_PATH] = {0}; + strncpy(dllpath, __FILE__, strlen(__FILE__)-strlen(__FILENAME__)-1); // get path + strncpy(mbpluginpath, dllpath, strlen(dllpath)-strlen(strrchr(dllpath, '/'))); // get path/.. + for(int i=0;mbpluginpath[i]!='\0';i++){mbpluginpath[i] = mbpluginpath[i]=='/' ? '\\' : mbpluginpath[i];} // replace '/' -> '\\' // Set RequestVariable environment char RequestVariable[MAXBUF]={0}; snprintf(RequestVariable, MAXBUF, "RequestVariable=%s", request); @@ -43,7 +51,7 @@ int callInterpreter(char *request, char *dllname, char *response, int responseSi PROCESS_INFORMATION pi = { }; char lpCommandLine[MAXBUF] = {0}; - snprintf(lpCommandLine, MAXBUF, "c:\\mbplugin\\plugin\\mbplugin.bat %s", dllname); + snprintf(lpCommandLine, MAXBUF, "%s\\plugin\\mbplugin.bat %s", mbpluginpath, dllname); ok = CreateProcess(NULL, lpCommandLine, NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi); if (ok == FALSE) return -1; @@ -80,8 +88,8 @@ __declspec(dllexport) void IssaPlugin (char *cmd, char *request, char *result, l char buf[MAXBUF]={0}; char resultInfo[MAXBUF]={0}; char dllname[MAX_PATH]={0}; - strncpy(dllname,__FILE__,strlen(__FILE__)-2); - snprintf(resultInfo, MAXBUF, "\nDLL %s\n%s\nArtyLa\n21.05.2020\n", dllname, dllname); + strncpy(dllname, __FILENAME__ , strlen(__FILENAME__)-2); + snprintf(resultInfo, MAXBUF, "\nDLL %s\n%s\nArtyLa\n%s\n", dllname, dllname, __DATE__); if(strcmp(cmd,"Info")==0){ snprintf(result, resultsize, "%s" ,resultInfo); // cmd==Info } diff --git a/dllsource/tst.bat b/dllsource/tst.bat index f961cd0..ba851ea 100644 --- a/dllsource/tst.bat +++ b/dllsource/tst.bat @@ -1,10 +1,15 @@ +%~d0 +cd %~dp0 +set FULLPATH=%~dp0 + if "%1"=="" GOTO :EOF if "%1"=="dllplugin" GOTO :EOF del *.d* del test_dllplugin.exe copy dllplugin.c %1.c -..\tcc\tcc -shared %1.c -..\tcc\tcc test_dllplugin.c %1.def +..\tcc\tcc -shared %FULLPATH%%1.c +..\tcc\tcc %FULLPATH%test_dllplugin.c %1.def test_dllplugin.exe +del %1.c del *.d* del test_dllplugin.exe diff --git a/plugin/dll_call_test.py b/plugin/dll_call_test.py new file mode 100644 index 0000000..69e0813 --- /dev/null +++ b/plugin/dll_call_test.py @@ -0,0 +1,35 @@ +import rlcompleter, readline;readline.parse_and_bind('tab:complete') +import os, sys, platform, ctypes + +def dll_call(plugin, cmd, login, password): + dllpath = f"..\\dllplugin\\{plugin}.dll" + if not os.path.exists(dllpath): + return f'{dllpath} not found' + if platform.architecture()[0] != '32bit': + return f'You are use python not a 32bit: {platform.architecture()[0]}' + dll = ctypes.CDLL(dllpath) + request = f'''\n + \n + 007F09DA\n + {login}\n + {password}\n + ''' + + buf = ctypes.create_string_buffer(1024) + dll.IssaPlugin.argtypes = [ctypes.c_char_p,ctypes.c_char_p,ctypes.c_char_p,ctypes.c_long] + dll.IssaPlugin.restype = None + dll.IssaPlugin(ctypes.c_char_p(cmd.encode('cp1251')),ctypes.c_char_p(request.encode('cp1251')),buf,len(buf)) + + return buf.value.decode('cp1251') + +if __name__ == '__main__': + + if len(sys.argv) != 1+4: + print(f'usage: {sys.argv[0]} plugin cmd(Info or Execute) login passw') + sys.exit() + plugin = sys.argv[1] + cmd = sys.argv[2] + login = sys.argv[3] + password = sys.argv[4] + + print(dll_call(plugin, cmd, login, password)) diff --git a/plugin/mbplugin.bat b/plugin/mbplugin.bat index 273585a..d74d70d 100644 --- a/plugin/mbplugin.bat +++ b/plugin/mbplugin.bat @@ -1,2 +1,5 @@ @echo off -C:\mbplugin\python\python C:\mbplugin\plugin\mbplugin.py %1 \ No newline at end of file +%~d0 +cd %~dp0 + +..\python\python ..\plugin\mbplugin.py %1 \ No newline at end of file diff --git a/plugin/mbplugin.py b/plugin/mbplugin.py index 0fd7681..81fb72e 100644 --- a/plugin/mbplugin.py +++ b/plugin/mbplugin.py @@ -19,7 +19,7 @@ def result_to_xml(result): def main(): - logging.basicConfig(filename="c:\\mbplugin\\log\\mbplugin.log", level=logging.INFO, + logging.basicConfig(filename="..\\log\\mbplugin.log", level=logging.INFO, format=u'[%(asctime)s] %(levelname)s %(funcName)s %(message)s') # В коммандной строке указан плагин ? if len(sys.argv) < 2: diff --git a/plugin/personalsetting.py.example b/plugin/personalsetting.py.example index 2e575bc..8459bd4 100644 --- a/plugin/personalsetting.py.example +++ b/plugin/personalsetting.py.example @@ -5,4 +5,5 @@ stocks= { 'BROKER_RU': {'STOCKS':(('AAPL',1,'Y'),('TATNP',16,'M'),('FXIT',1,'M')), 'REMAIN': {'USD':5, 'RUB':536}, 'CURRENC': 'USD'} } -SQLITESTORE = False # Сохранять параллельно балансы в БД SQLITE ? \ No newline at end of file +SQLITESTORE = not False # Сохранять параллельно балансы в БД SQLITE ? +SQLITEPATH = r'..\DB\BalanceHistory.sqlite' diff --git a/plugin/store.py b/plugin/store.py index 241f887..40c273e 100644 --- a/plugin/store.py +++ b/plugin/store.py @@ -2,8 +2,8 @@ import sys;sys.dont_write_bytecode = True import os,sys, pickle, requests -storefolder = 'C:\\mbplugin\\store' -storename = 'C:\\mbplugin\\store\\persistent_store' +storefolder = '..\\store' +storename = '..\\store\\persistent_store' def get_from_store(key): diff --git a/plugin/test2.py b/plugin/test2.py index a1ae8d9..9691957 100644 --- a/plugin/test2.py +++ b/plugin/test2.py @@ -33,8 +33,8 @@ def get_balance(login, password, storename=None): captcha_text = '' response1_text = 'CAPTCHA' url = 'https://lk.megafon.ru/login/' - cap_urls = [i for i in bs4.BeautifulSoup( - response1_text).findAll('img') if i['alt'] == 'CAPTCHA'] + img_tag = bs4.BeautifulSoup(response1_text, 'html.parser').findAll('img') + cap_urls = [i for i in img_tag if i['alt'] == 'CAPTCHA'] if len(cap_urls) > 0: captcha_url = urlparse.urljoin(url, cap_urls[0]['src']) buffer = session.get(captcha_url).content diff --git a/plugin/test_mbplugin_dll_call.bat b/plugin/test_mbplugin_dll_call.bat new file mode 100644 index 0000000..7b52201 --- /dev/null +++ b/plugin/test_mbplugin_dll_call.bat @@ -0,0 +1,16 @@ +@ECHO OFF +%~d0 +cd %~dp0 + +if "%~1"=="" goto NOPARAM +if "%~2"=="" goto NOPARAM +if "%~3"=="" goto NOPARAM +echo INFO: +..\python\python.exe ..\plugin\dll_call_test.py %1 Info %2 %3 +echo EXECUTE: +..\python\python.exe ..\plugin\dll_call_test.py %1 Execute %2 %3 +pause + +goto :EOF +:NOPARAM +ECHO Use %0 p_plugin login pass \ No newline at end of file diff --git a/plugin/test_mbplugin_login_pass.bat b/plugin/test_mbplugin_login_pass.bat index 33e0e9b..95c5051 100644 --- a/plugin/test_mbplugin_login_pass.bat +++ b/plugin/test_mbplugin_login_pass.bat @@ -1,9 +1,12 @@ @ECHO OFF +%~d0 +cd %~dp0 + if "%~1"=="" goto NOPARAM if "%~2"=="" goto NOPARAM if "%~3"=="" goto NOPARAM SET RequestVariable="007F09DA%2%3" -call C:\mbplugin\plugin\mbplugin.bat %1 +call ..\plugin\mbplugin.bat %1 pause goto :EOF diff --git a/plugin/test_mbplugin_set.bat b/plugin/test_mbplugin_set.bat index 431d2f8..e69f6c8 100644 --- a/plugin/test_mbplugin_set.bat +++ b/plugin/test_mbplugin_set.bat @@ -1,2 +1,6 @@ +@echo OFF +%~d0 +cd %~dp0 + @SET RequestVariable="007F09DAloginloginpassword123456" -@C:\mbplugin\plugin\mbplugin.bat p_test \ No newline at end of file +@..\plugin\mbplugin.bat p_test \ No newline at end of file diff --git a/python/get_python.bat b/python/get_python.bat index de8ada7..dfd0648 100644 --- a/python/get_python.bat +++ b/python/get_python.bat @@ -1,21 +1,24 @@ -c: -mkdir C:\mbplugin\python -cd C:\mbplugin\python +%~d0 +cd %~dp0 -@REM C:\mbplugin\python:https://www.python.org/ftp/python/3.8.3/python-3.8.3-embed-win32.zip -curl -LOk https://www.python.org/ftp/python/3.8.3/python-3.8.3-embed-win32.zip -7z x python-3.8.3-embed-win32.zip +@REM python __pycache__ +set PYTHONDONTWRITEBYTECODE=x -@REM https://bootstrap.pypa.io/get-pip.py C:\mbplugin\python -curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py +@REM mbplugin\python:https://www.python.org/ftp/python/3.8.3/python-3.8.3-embed-win32.zip +if not exist python38.zip curl -LOk https://www.python.org/ftp/python/3.8.3/python-3.8.3-embed-win32.zip +if not exist python38.zip 7z x python-3.8.3-embed-win32.zip +if exist python-3.8.3-embed-win32.zip del python-3.8.3-embed-win32.zip -@REM C:\mbplugin\python -python get-pip.py +@REM https://bootstrap.pypa.io/get-pip.py mbplugin\python +if not exist get-pip.py curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py -@REM C:\mbplugin\python\python38._pth ( #) import site +@REM mbplugin\python +if not exist Scripts\pip.exe python get-pip.py + +@REM mbplugin\python\python38._pth ( #) import site python -c "d=open('python38._pth').read();open('python38._pth','w').write(d.replace('#import site','import site'))" -@REM C:\mbplugin\python +@REM mbplugin\python python -m pip install requests pillow beautifulsoup4 pyodbc pyreadline @REM tkinter python diff --git a/readme.md b/readme.md index 319c043..fcd6826 100644 --- a/readme.md +++ b/readme.md @@ -6,37 +6,41 @@ DLL шлюз дает возможность писать собственные ## Установка Вариант 1 Готовый архив (простой) Архив можно найти в [releases](https://github.com/artyl/mbplugin/releases) на github или в форуме на [4pda](https://4pda.ru/forum/index.php?showtopic=985296) посвященном MobileBalance. -Архив распаковать в папку C:\mbplugin +Архив распаковать, после распаковки если путь отличается от c:\mbplugin необходимо запустить dllsource\compile_all_p.bat, после этого текущие пути будут прописаны в DLL ## Установка Вариант 2 Из github -Склонировать репозиторий в папку C:\mbplugin -git clone C:\mbplugin +Склонировать репозиторий c github +``` +git clone +``` загрузить и распаковать tcc и python: -TCC: C:\mbplugin\tcc\get_tcc.bat -PYTHON: C:\mbplugin\python\get_python.bat -tkinter для python, если нужен ввод капчи для python к сожалению автоматом поставить не получиться, нашел только [такой](https://stackoverflow.com/questions/37710205/python-embeddable-zip-install-tkinter) -Сборка всех DLL: C:\mbplugin\dllsource\compile_all_p.bat -После этого все DLL будут находится в папке C:\mbplugin\dllplugin -Если есть желание использовать свой питон, тогда можно поменять вызов в C:\mbplugin\plugin\mbplugin.bat +``` +tcc\get_tcc.bat +python\get_python.bat +``` +tkinter для python, если нужен ввод капчи для python к сожалению автоматом поставить не получиться, нашел только [такой способ](https://stackoverflow.com/questions/37710205/python-embeddable-zip-install-tkinter) +Сборка всех DLL: dllsource\compile_all_p.bat +После этого все DLL будут находится в папке mbplugin\dllplugin +Если есть желание использовать свой питон, тогда можно поменять вызов в mbplugin\plugin\mbplugin.bat ## Использование. -Пути, по крайней мере пока, жестко захардкожены и все должно лежать именно в такой структуре в папке C:\mbplugin +Пути, жестко прописываются в DLL, в готовой сборке они смотрят в C:\mbplugin, если ваша папка отличается от этой, то запустите dllsource\compile_all_p.bat Подключить DLL для нужных провайдеров (Настройки\Плагины\Операторы Добавить и выбрать DLL для нужных операторов) В настройках для соответсвующего телефона выбрать провайдера соответствующей DLL ## На данный момент реализованы плагины: (Источником информации послужили как собственное изучение так и существующие плагины, так что пользуясь случаем хочу выразить благодарность всем авторам leha3d Pasha comprech y-greek и другим, кто тратил свои силы и время на реверс сайтов операторов и разработку) -test1 - Простой тест с демонстрацией всех полей (правда оказалось, что MB из DLL принимает только Balance Expired Min Internet TarifPlan BlockStatus AnyString) +test1 - Простой тест с демонстрацией всех полей (MB из DLL принимает далеко не все Balance Expired Min Internet TarifPlan BlockStatus AnyString) test2 - Пример реализации ввода капчи +mts - МТС beeline - Билайн -cardtel - Cardtel (IP телефония) megafon - Мегафон -mts - МТС -sodexo - Получение баланса карты Sodexo (подарочные карты) -strelka - Баланс карты стрелка tele2 - ТЕЛЕ2 +strelka - Баланс карты стрелка zadarma - Zadarma.com (IP телефония) +cardtel - Cardtel (IP телефония) +sodexo - Получение баланса карты Sodexo (подарочные карты) usd - Курс доллара с RBC eur - Курс евро с RBC financeyahoo - Курс ценных бумаг с finance.yahoo.com @@ -45,36 +49,46 @@ stock - Рассчет цены портфеля ценных бумаг sipnet - Sipnet (IP телефония) avtodor-tr - Автодор транспондер -## Как проверить вручную -запустите из C:\mbplugin\plugin: -C:\mbplugin\plugin\test_mbplugin_login_pass.bat p_test login password +## Как проверить что все работает +### ВАЖНО! +DLL собраны 32 битном компиляторе, и вызываться должны из 32 битного приложения 32 битную DLL вызвать из 64 битного приложения нельзя. +### Проверка самих плагинов +запустите из mbplugin\plugin: +test_mbplugin_login_pass.bat p_test1 login password Если в консоль будет выведен XML, то скорее всего у вас все работает: ``` 124.45 .... ``` Таким же образом можно проверить любой плагин: C:\mbplugin\plugin\test_mbplugin_login_pass.bat p_[имя плагина на python] [логин] [пароль] +### Проверка DLL +запустите из mbplugin\plugin: +plugin\test_mbplugin_dll_call.bat test1 123 456 +Должен выдать XML с инфо о плагине и XML с балансом +При желании так можно проверить любую DLL но учитывая, что они собираются пачкой, то и с остальными все ок. +Если возникли ошибки, попробуйте выполнить dllsource\compile_all_p.bat ## Как это работает Mobilebalance вызывает DLL передавая ей логин и пароль через xml строку -DLL вызывает C:\mbplugin\plugin\mbplugin.bat передавая ему имя плагина в качестве параметра, а переданный XML через переменную окружения RequestVariable -mbplugin.bat вызывает mbplugin.py в котором вызывается соответсвующий DLL плагин. +В самой DLL никакого функционала нет, она служит лишь оберткой для передачи вызова в mbplugin\plugin\mbplugin.bat +DLL вызывает mbplugin\plugin\mbplugin.bat передавая ему имя плагина в качестве параметра, а переданный XML через переменную окружения RequestVariable +mbplugin.bat вызывает mbplugin.py в котором вызывается соответсвующий python плагин. mbplugin.bat возвращает результат через stdout. ## Почему так сделано. Данные по параметрам вызова DLL были получены с помощью реверса существующего DLL плагина. Я постарался сделать все так, чтобы все можно было собрать за 10 минут не устанавливая 10 гигабайтные компиляторы, минималистичная DLL весь код вынесен в скрипты. Конечно DLL можно собрать и на vc и gpp и на Delphi и много на чем еще, -но для этого нужно много возни с установкой среды, в моем варианте все можно собрать с нуля за 10 минут скачав несколько десятков мегабайт. -DLL мог без труда любой желающий и убедится что в ней нет закладок. +но для этого нужно много возни с установкой среды, в моем варианте все можно собрать с нуля за 10 минут скачав несколько десятков мегабайт. +Количество кода для DLL минимум, чтобы любой желающий мог без труда убедится что в нем нет закладок. + Остальной код на скриптах, и его можно проверить в любой момент. C я знаю не очень хорошо, поэтому единственный простой способ передать request я нашел через переменную окружения, а возврат осуществляется через поток вывода. -Пути прописаны абсолютными, потому что из DLL выяснить по какому пути она находится оказалось очень нетривиальной задачей, даже имя ее узнать очень непросто. -Хотел сохранить настройки путя в реестре, но из реестра прочитать оказалось тоже не просто. -В у меня получилось сделать только так, код на C получился достаточно корявый, но у меня вроде работоспособен. +Пути в папке mbplugin внутри DLL приходится прописывать абсолютными, потому что из DLL выяснить по какому пути она находится оказалось очень нетривиальной задачей, даже имя DLL узнать очень непросто. В принципе это проблема небольшая на tcc все пересобирается за пару секунд. +Хотел сохранить настройки для путей в реестре, но из реестра прочитать оказалось тоже не просто. +В результате у меня получилось сделать только так, код на C получился не очень хороший, но работоспособный. Если кто в состоянии причесать сишный код, буду признателен. -Вызов bat файла а не python сделан для того чтобы если будет желание отвязаться от python -с минимумом изменений в остальном коде. +Вызов bat файла а не напрямую скрипта python сделан для того чтобы если будет желание отвязаться от python, с минимумом изменений в остальном коде. Т.к. в запросе передается только логин и пароль, то нам приходится сделать по отдельной DLL для каждого сервиса Чтобы оставить задел на будущее я для плагинов добавил еще префиксы, для питона это p_ @@ -83,27 +97,22 @@ C я знаю не очень хорошо, поэтому единственн tele2 - имя модуля на python который получает баланс p_tele2.dll - dll которую мы подключаем в mobilebalance -Не все поля оказывается понимает, как оказалось, например не понимает SMS, -Насколько я понял воспринимает только: +В процессе эксплуатации выяснилось, что mobilebalance оказывается все поля принимает из результата, как оказалось, например не понимает SMS, +Список принимаемых полей достаточно короткий: Balance Expired Min Internet TarifPlan BlockStatus AnyString Все кроме этого будет проигнорировано -Несмотря на то что на первый взгляд это все идет не через браузер mobilebalance все равно перед стартом DLL -дергает движок IE (res://ieframe.dll/navcancl.htm и about:blank) - это видно по логу и появлению файлов -в папке кэша IE, так что не исключаю, что часть каких-то глюков может по прежнему лечиться чисткой кэша -браузера, хотя это и маловероятно. - -Все это добро выложил на [github](https://github.com/artyl/mbplugin) -Там только исходники, после загрузки проекта поместить его в папку C:\mbplugin +Несмотря на то что на первый взгляд это все идет не через браузер mobilebalance все равно перед стартом DLL дергает движок IE (res://ieframe.dll/navcancl.htm и about:blank) - это видно по логу и появлению файлов в папке кэша IE, так что не исключаю, что часть каких-то глюков может по прежнему лечиться чисткой кэша браузера, хотя это и маловероятно. ## Как написать свой плагин Если на python, то это файл с функцией get_balance(login, password, storename) -storename нужен для хранения сессии, формируется как имя плагина + login +storename это строка, используемая как ключ для хранения сессии. Формируется как имя плагина + login Функция возвращяет результат в виде словаря. -После того как плагин будет готов запускаем C:\mbplugin\dllsource\compile_all_p.bat и будут пересобраны DLL для всех имеющихся в папке C:\mbplugin\plugin плагинов +Можно посмотреть как сделаны другие плагины, также есть простые тестовые. +После того как плагин будет готов запускаем C:\mbplugin\dllsource\compile_all_p.bat и будут пересобраны DLL для всех имеющихся в папке C:\mbplugin\plugin плагинов. Подключаем полученную DLL в MobileBalance и используем ее для получения баланса. -ВАЖНО. В xml который возвращает плагин поля case sensitive, так что если будет balance вместо Balance -MB будет писать что баланс равен нулю. + +ВАЖНО. В xml который возвращает плагин поля case sensitive, так что если будет balance вместо Balance MB будет писать что баланс равен нулю. --- Структура request который приходит из mobilebalance через переменную окружения ---------- ``` diff --git a/tcc/get_tcc.bat b/tcc/get_tcc.bat index 74497f8..465d795 100644 --- a/tcc/get_tcc.bat +++ b/tcc/get_tcc.bat @@ -1,6 +1,6 @@ -c: -mkdir C:\mbplugin\tcc -cd C:\mbplugin\tcc +%~d0 +cd %~dp0 + curl -LOk http://download.savannah.gnu.org/releases/tinycc/tcc-0.9.27-win32-bin.zip 7z x tcc-0.9.27-win32-bin.zip -o.. del tcc-0.9.27-win32-bin.zip \ No newline at end of file