diff --git a/UnGUI/!/cmd launcher.todo b/UnGUI/!/cmd launcher.todo new file mode 100644 index 0000000..0d7f99c --- /dev/null +++ b/UnGUI/!/cmd launcher.todo @@ -0,0 +1,74 @@ + +Хотелки: ++* возможность ставить окно поверх всех + * запоминание предыдушего положения окна (опционально) + * автостарт по таймеру (опционально) + * Drag'n'Drop файлов в специально отведённые места (например, в edit'ы, чтобы не писать утомительные пути) ++* поддержка загрузки разных конфигов для разного набора программ + * отдельный конфиг с настройками + * возможность схоронять настройки при выходе + * перенаправление вывода консольных программ в отдельный лог/отдельное окно (опционально) + * в случае перенаправления в окно — подсветка по регэкспам (как в zsh [или fish. Хуй проссышь, что там и где]) + * вкорячивание параметров в отдельное место (например, в середину конвейера) e.g: cat %p | tail + * расширенное вкорячивание параметров cat %p1 | tail %p2 : %p1 — место для подстановки первого параметра, %p2 — место для подстановки второго параметра + * в поле выбора конфигов сделать поиск. Строка поиска должна по мере набора писаться в заголовке программы (e.g. 'Текущий фильтр: "азаза глаза"'), а результаты выпадающего списка динамически изменяться ++* сделать поле для разделителя параметров. Мало ли понадобится соединять аргументы (в роли которых будут команды) на пробелом а, например, " && ". Как вариант, ввести в группу ключ "group_prefix" ++* вместо радиобаттонов — выпадающие списки (может быть неудобно из-за того, что параметров не будет видно. С другой стороны появится больше степеней свободы. Поэтому ``positive vote'') + * автодополнение в комбобоксах по DlgDirListComboBox ++* поддержка многоязычности + * возможность писать несколько локализованных версий названий пунктов. e.g. value_1_rus, value_1_ukr, value_1 (а это английский); hint_1_rus, hint_1_ukr, hint_1 + * запилить Trackbar (ползунок) + * запилить штуку, которая будет по нажатию ArrowUp/Down ин/декрементировать численное значение поля. Цифири подставлять. Гибрид Up-Down и Edit ++* возможно переименовать из "cmd launcher" в "Universal GUI" ++* сделать встраивание конфига в ресурсы при помощи ключа -e[mbed] + * к комбобоксах для указания пути прикрутить индикатор с красным восклицательным знаком, который будет показываться, если путь, содержащийся в элементе, не существует + * + +Полу-идеи: + * графический ёба-редактор конфига + * подумать над тем, как можно использовать BS_AUTO3STATE + * подумать над приоритетом встроенного конфига и внешнего. Пока склоняюсь к тому, что внешний > встроенный. Удобно для отладки. Ну и это повысит неубиваемость, коея должна стать легендарной + +Желаемые переключатели комсторки: ++ -c[onfig] - выбор конфигурационного файла ++ -t[op] <0|1> - поверх всех окон ++ -l[anguage] - выбор языка <3-хсимвольная ISO3166 аббревиатура или LangID от Микрософта> +-+ -a[utoclose] - автоматическое закрытие программы после нажатия на "Запустить" + + ... — просто файл, который будет подставляться в конец всех параметров. Будет КРАйнЕ БЛЯТЬ полезно для перетаскивания + файлов на программу. Сделал Drag'n'Drop — и вуаля — открылось окно с настройками для перекодирования в ffmpeg + с уже подставленным на нужное место* файлом для обработки. + Ещё один охуенный юзкейс — Drag'n'Drop нескольких файлов, один из которых — конфиг (например, и именем вида "*.clcnf.ini") + так, в зависимости от перетаскиваемого конфига с фодними и теми же файлами можно будет сделать овердохуя действий ++ -? -h -help /? /h /help --? --h --help — ко-ко-кошко со справкой +-+ -e[mbed], -r[eplace], -l[ist], -d[ump] — управление ресурсами + +* — !подумать, как это самое нужное место определить, ведь тот же ффмпег жуёт уравнения только вида "ffmpeg -ss 00:00:02 -i -c copy ... ... ...", + где пресловутый находится не иначе как в середине строки, окруженный параметрами. + Варианты: 1. Специальный токен (например, %cp1, %cp2, ..), вводимый в Edit + 2. Специальная группа с типом, например, "cmd_line_param" (более туманное решение) + + +Реализовано: + + выход по Esc + + парсинг конфига + + всплывающие подсказки + + ура! есть ёба поле с текстом, куда генерируется командная строка, состоящая из отмеченых пользователем параметров + + manifest + + базовая поддержка локализации — секция [lang] в конфигурационном файле + + теперь наконец работает кнопка "Запустить" + + расширенная поддержка локализации — считывание системной локали и поиск нужных секций в конфиге. В случае неудачи, всегда остаётся намертво вкомпилированный английский + + размер окна изменяется в зависимости от количества параметров в конфиге + + объединение группы опций в GroupBox — рамочку + + подсказка для групбокса в виде кнопки со знаком вопроса + + работающие радиокнопки (пришлось писать отдельный обработчик нажатий на них, ибо все кнопки на форме по умолчанию система связывала в один огромный переключатель) + + работающая опция onTop (переименовано в on_top) + + добавлена VersionInfo + + теперь есть рабочая опция exit_after_start + + за неимением интернетов, чтоб утащить (видел её у одного украинца), пришлось писать свою реализацию CommandLineToArgvA + + переключатели -c -t -l + + возможность встраивать конфиг внутрь программы при помощи ключа -e. Есть минимальный детект UPX`a и алгоритм рас/запаковки с его участием + + поправлено поведение окна с ошибками + + теперь можно создавать combobox`ы + + появились ключи prefix_group и prefix. Назначение очевидно. Второй подставляется перед каждым ненулевым элементом, первый — если группа не пустая. + + diff --git a/UnGUI/!/config_demo.ini b/UnGUI/!/config_demo.ini new file mode 100644 index 0000000..c2148fc --- /dev/null +++ b/UnGUI/!/config_demo.ini @@ -0,0 +1,155 @@ +; Demo version^W file + +; Portion of default OS шindoшs hotkeys ^___^ +; Tab — next element +; Shift+Tab — previous element +; Up/Down — previous/next element on radiobutton +; Alt+ArrowDown — expand dropdown combobox + + +[options] +title =Wishmaster 2000 +command =hui.exe +on_top =true +exit_after_launch=true + + +[param_group_1] +title =Checkbox group +hint =This is Checkbox group hint +type =checkbox + +value_1 =Checkbox #1 +key_1 =cb11 +default_1 =true +hint_1 =This option add "cb11" to resulting cmdline + +key_2 =cb12 +hint_2 =This option add "cb12" to resulting cmdline + +key_3 =cb13 + + +[param_group_2] +; If "type" not set, we think that you thought about checkbox +key_1 =cb21 +hint_1 =This is standalone checkbox + + +[param_group_3] +title =Radio group +hint =This is Radio group hint. This group switches "rb31" and "rb32" +type =radio + +value_1 =Radio option #1 +key_1 =rb31 +hint_1 =This option add "rb31" to resulting cmdline + +key_2 =rb32 +default_2 =1 + + +[param_group_4] +title =Static group +type =static +hint =There is no spoon + +value_1 =Line with wise text +value_2 =Second line + +hint_1 =!!! S-P-O-O-N !!! +hint_2 =Hints doesn't work on Static + + +; order isn't needed. Author is not perfectionist. I'm depressionist. +[param_group_6] +type =static +key_1 =This is group without +key_2 =Title and group hint + + +[param_group_5] +title =Will not be shown because of 1 element in section +hint =Will not be shown because of 1 element in section +type =static + +value_1 =Joke-oneliner + + +[param_group_7] +; empty group adds space between neighbours + + +[param_group_8] +title =Edit group +type =edit + +value_1 =ed41 +hint_1 =This hint explains sense of first Edit field + +key_2 =This is unused value. Just demo, the way how to create empty Edit element + +value_3 =ed43 +;These Edits will not be shown 'coz "value_n" numeration broken: "value_4" lacked +value_5 =ed45 +value_6 =ed46 + + +[param_group_9] +title =Choose your chair: +hint =Kukarek +type =radio + +; ampersand points at hotkey symbol +value_1 =Shaped s&pades +key_1 =--spades +default_1 =1 +hint_1 =This option can be choosen by hotkey Alt+P + +value_2 =Jerked c&ocks +key_2 =--cocks +; two default radiobuttons. Of course, it's not normal. But will work as expected +default_2 =1 +hint_2 =This option can be choosen by hotkey Alt+O + + +[param_group_10] +title =Destination: +hint =Kuda-h +type =combobox + +value_1 =Default City +key_1 =--msk +; among two (or more) defaults will be chosen teh first one +default_1 =1 + +value_2 =Default City 2 +key_2 =--spb +; DS so DS +default_2 =1 + +; New Vasiouki — element that means nothing +value_3 =New Vasiouki + +; Muhosran' has no name +key_4 =--mhs + + +[param_group_12] +; This group never reach your screen 'coz [param_group_11] had not been created +type =checkbox +key_1 =--very-important-flag +default_1 =true + + + + +; An example of localization. You may force it with renaming this section to [lang] +[lang_rus] +tooltip_edit_exePath=Путь к исполнимому файлу +tooltip_edit_cmdLine=Сгенерированная командная строка +btn_start=&Запустить +tooltip_btn_start=Жать сюда +error_no_file=Файл "%s" не найден +error_no_path=Указанный путь не найден +error=Ошибка diff --git a/UnGUI/!/config_explained_rus.ini b/UnGUI/!/config_explained_rus.ini new file mode 100644 index 0000000..cdb0b4b --- /dev/null +++ b/UnGUI/!/config_explained_rus.ini @@ -0,0 +1,164 @@ +; Самоочевидные опции: +[options] +; Поверх всех окон +on_top =true +; Выход после безошибочного запуска программы +exit_after_launch=true +; Схоронять натыканные галочки при выходе +; Не реализовано +;save_settings_on_exit=1 + +; То, что пойдёт в заголовок окна +title =Yoba 2000 +; Путь к исполняемому файлу +command =hui.exe + +[param_group_1] + +; ~~~~~~~~~~~~ ОБЩИЕ ПАРАМЕТРЫ ГРУППЫ ~~~~~~~~~~~~ + +; заголовок группы +title =Optimizations +; подсказка для группы (отображается в виде кнопки "?" справа от заголовка) +hint =EEnadblee + +; тип элементов +; checkbox - галочка +; edit - поле для ввода +; static - декоративное поле с надписью +; radio - переключатель с точкой (в такой группе нужен хотя бы один элемент +; с default=true. В противном случае, групппа не будет активироваться +; по нажатию Tab) +type =checkbox + +; ~~~~~ ПАРАМЕТРЫ ОТДЕЛЬНЫХ ЭЛЕМЕНТОВ ГРУППЫ ~~~~~ + +; единственный обязательный ключ при задании параметра - key_n +; единственный обязательный ключ при задании параметра группы edit'ов и static'ов - value_n +; обязательные ключи должны содержать хоть какое-нибудь значение +; если ключа нет или он пуст, ВСЕ СЛЕДУЮЩИЕ ЭЛЕМЕНТЫ ГРУППЫ БУДУТ ПРОИГНОРИРОВАНЫ + +; _1 - означает первый параметр в группе +; value - то, что будет написано в элементе. +; & (амперсанд), поставленный перед символом, создаёт горячую клавишу Alt+Символ +; в примере ниже, чекбокс будет активироваться по нажатию Alt+h +value_1 =&htt + +; то, что будет подставлено в реузльтирующую командную строку +key_1 =--oh + +; будет ли стоять галочка при запуске: +; -- будет - 1, true +; -- не будет - 0, false, всё остальное, ключ отсутствует +default_1 =true + +; подсказка, всплывающая по наведению мыши на элемент +hint_1 =hyperthreading + + +; тут вместо "sse3" на чекбоксе будет написано значение key_2, то есть "--os3" +;value_2 =sse&3 +key_2 =--os3 +default_2 =0 +hint_2 =3-rd level + +; можно задавать элемент и при помощи одного ключа: +key_3 =--0s4 + +[param_group_2] +; если ключа type нет, по умолчанию подставляется checkbox. +key_1 =-hui +default_1 =true + +[param_group_3] +title =Buharin +type =radio +key_1 =xwqij +default_1 =1 +key_2 =lodj + +[param_group_4] +title =Buharin +hint =Co-co +type =edit +value_1 =s +value_2 =lodj + +[param_group_5] +type =edit +value_1 =текст1 +; value_3 не будет отображён, так как отсутствует value_2 +value_2 =текст2 +value_3 =текст3 + +[param_group_6] +title =Buharin +hint =Co-co +type =radio + +value_1 =lodj +key_1 =--sol1 +default_1 =1 + +value_2 =lodj +key_2 =--sol2213 +;default_2 =1 + +[param_group_7] +title =Buharin +hint =Co-co +type =radio + +value_1 =lodj +key_1 =--sol1 +default_1 =1 + +value_2 =lodj +key_2 =--sol2213 +;default_2 =1 + + + + + + + + + + + + + + + + + + +; section [lang_ZZZ] - ZZZ - ISO3166 language code +; fallbacks: section [lang_NNNN] - NNNN - decimal LangID +; section [lang] +; built-in English (which don't need any kind of section) +; +; e.g. section seach order for user with Russian language: +; [lang_rus] > [lang_1049] > [lang] > -no section- (will be used built-in English) +; if string wouldn't found then English substitute'll be used + +[lang_rus] +tooltip_edit_exePath=Путь к исполнимому файлу +tooltip_edit_cmdLine=Сгенерированная командная строка +btn_start=&Запустить +tooltip_btn_start=Жать сюда +error_no_file=Файл "%s" не найден +error_no_path=Указанный путь не найден +error=Ошибка + +; English (in fact, we have one built-in, so this section for translators use only) +[lang] +tooltip_edit_exePath=Path to executable file +tooltip_edit_cmdLine=Generated command line +btn_start=&Start +tooltip_btn_start=Press here +error_no_file=File "%s" not found +error_no_path=Path not found +error=Error +error_unknown=Unknown error 0x%08X \ No newline at end of file diff --git a/UnGUI/!/plan.txt b/UnGUI/!/plan.txt new file mode 100644 index 0000000..eeec873 --- /dev/null +++ b/UnGUI/!/plan.txt @@ -0,0 +1,112 @@ +Аноним (Microsoft Windows 7: Chromium based) 18/12/17 Пнд 20:31:59 №2213110 + +/r утилитку для шиндовс, реализующую универсальный гуй для для CLI-софтин, чтобы просто конфигурировалась файликом типа .ini с секциями вида: + +[program_name_1] +bin_path=C:\program.exe + +option_tittle_1=Optimizations +option_hint_1=Enable cpu optimizations +option_type_1=combobox +option_values_1=htt,sse3,sse4 +option_default_1=1,2 +option_keys_1=--oh,--os3,--0s4 + +option_tittle_2=Encoder type B +option_hint_2=Use single thread encoder +option_type_2=checkbox +option_default_2=0 +option_keys_2=--etb + +[program_name_2] +... + +Ну короче вы поняли, сам бы написал, да начал уже сишку с winapi забывать т.к. с 2006 ничего не кодировал. + +_____________________ + +Аноним (Microsoft Windows 7: Chromium based) 19/12/17 Втр 18:41:16 №2213833 + +Я ждал тебя, анон! Если получится, то получишь мощнейшую дозу облучения вселенским добром! + +Честно я удивлен, что никто не написал подобное. + +Как всегда в процессе реализации вскроются многие подводные камни, так что будь готов. А у меня, пока это крутилось в голове пару дней, назрели некоторые очертания этих камней и уточнения - и чувствую что это далеко не всё, с чем тебе придётся столкнуться, я думаю ты сам уже всё это додумал, но возможно тебе пригодится: + +1) Мб лучше будет реализовать конфиг для каждой консольной проги не секциями в одном файле, а отдельным файлом - так будет проще добавлять новые конфиги, скачал файлик кинул в каталог с прогой она при запуске нашла все эти конфиги в своем каталоге и поместила в выпадающий список (я вижу выбор консольный утилиты так - сверху окна - дропдаунлист с перечислением названий утилит - они же имена файлов конфига (а ведь класно же - мы получаем профили тут же - можно для того же ффмпега нахерачить кучу конфигов для разных задач), при выборе какого-то из них ниже динамически создаются контролы один над другим, предварительно уничтожаются старые, если таковые есть). + +2) В полях типа EDIT нужно предусмотреть флаг автоматического заключения в кавычки (для путей нужно) и фильтрации двойного заключения в кавычки (это если флаг стоит, а юзер руками добавил их ещё) + +3) Может быть необходимо реализовать зависимость доступности опций друг от друга, и обязательно использовать для этого логические операторы, ну типа опция С доступна если активированы опции A&&B или A||B или !A (по поводу отрицания надо подумать - может быть и не нужно ибо в большинстве случаев реализуемо с помощью Radio Buttons). И тут есть ньюанс, в случае если та опция, от которая зависит доступность текущей простая типа чекбокса, то всё ок, а если опция типа множественного выбора (в моем примере htt,sse3,sse4)? Простой вариант разбивать сложную опцию на простые. + +4) Запоминать последние параметры? + +Короче удачи тебе, анон. Очень полезная будет софтина - законфигурировать можно будет и всякие архиваторы, видеоенкодеры, компиляторы, ютьюбдлы, да даже хромиум удобно с теми или иными ключами запуска стартовать - у него их чуть ли не тысяча имеется. + +_____________________ + +Аноним (Microsoft Windows 7: Chromium based) 23/12/17 Суб 20:15:15 №2217060 + +Ну ты рили быстрый, я уже думал до окончания НГ праздников тебя не ждать. Хотя какие у двачеров/сосачеров праздники, я даже не пью. Токса у меня кстати нет - думал как-то завести, а потом подумал не стал - ибо давно давно ни с кем не общаюсь, даже в сети, кроме как здесь. Ну ты не торопись, если лень - жди очередного импульса творческой энергии, лучше всё это довести до более менее развитого состояния, хотя как известно программу нельзя закончить, лол а потом опубликовать на гитхабе (или как это называется - я старый прост, уже бросил увлечение программированием когда всё эти гихабы стали популярны, лишь пилю/допиливаю расширения для хрома, чисто для себя), софтина может стать весьма популярной, недавно вот опять полез в консольку, чтобы webp сконвертить в jpeg для моего древнего ACDSee 2004 года, который его не понимать. + +_____________________ + +Аноним (Microsoft Windows 7: Chromium based) 27/12/17 Срд 03:29:53 №2219648 + +Серьёзный вишмастер, почитал туду - не ожидал что такой основательный подход будет. +Кое-какие мысли возникли, может быть окажутся тебе полезными, да простят нас анонимусы, за использования этого треда как чатика: + +Я бы не усложнял себе жизнь кодингом обработки параметров собственной коммандной строки запуска, +мне кажется достаточно будет отедльного конфига. +Для случая, когда есть какой-то постояный набор входных данных достаточно будет запоминания последних параметров. +Можно какие-то основные параметры типа языка интерфейса выбирать через простую контекстную менюшку. +Алсо можно придумать свое расширение для конфигов и ассоциировать его с UnGUI, но не всем такой вариант понравится, +когда софт вносит изменения в настройки ОС, теряется некий "консольно-портативный" дух, хотя с другой стороны всё это ерунда. +----------- +В случае инпут-файлов действительно нужно предусмотреть опцию добавления ключа перед каждым выбранным/брошенным файлом, +Алсо туда же ouput, вот например "dwebp in_file [options] [-o out_file]", +т.е. наверное EDITам стоит добавить необязательные параметры: +key_n, +опцию "добавлять слитно без пробела" по умолчанию в false, и тип связанной кнопки который +может быть открытие файла/ов, сохранение файла, и выбор каталога. +----------- +>возможность схоронять настройки при выходе ++ +а если есть дефолтные, то и возможность сброса на них +----------- +>перенаправление вывода консольных программ в отдельный лог/отдельное окно (опционально) + +можно попробовать сформированную строку суммировать с "cmd.exe /k", хотя это костыльно как-то +Но лог в файл полезная всё равно штука +----------- +>графический ёба-редактор конфига +Ты просто застопоришься на этом и в итоге редактор станет твоим основным проектом, лол. +Нафиг не нужно имхо - юзеры которые будут готовить конфиги, скорее всего не обременятся поработать в текстовом редакторе один-два раза. +Алсо как прикольно получается - придумали утилиту которая по конфигу конструирует гуй, +теперь надо придумать утилиту, которая с помощью гуя будет формировать конфиг. Хотя возможно удобно да - как редакторе ресурсов в VS. +----------- + +_____________________ + +Аноним (Microsoft Windows 7: Chromium based) 28/12/17 Чтв 02:43:50 №2220203 +>>2220143 +>У меня тут чистая сишка и чистое WinAPI +>win95 +Может я тупой или уже сильно отстал, но в чём там сложности с контекстным меню (кмк под 95 нет смысла писать, или есть?)? На голом winapi и С я вроде когда-то реализовывал контекстные менюшки, начиная с винтукея WINVER=0x0500;_WIN32_WINNT=0x0500; усё работает, конечно, там будет достаточно объёмный код всей возни со структурами и флагами, но это же winapi, куда без этих структур и флагов. + +Запихивать конфиги в ресурсы хорошая идея, типа скачал и сразу имеешь конфиги в комплекте к популярным софтинам типам ffmpeg, нужно будет кодить их простенький менеджер - с функциями перечисления, удаления, добавления, перезаписи/обновления. По поводу исполняемых хз, кажется это лишнее, как там на это антивирусы, норм? Хотя да - во всяких процессэксплорерах драйвера вшиты. Потом возникают лишние движения в плане поддержания новых версий - помимо скачивания новой версии какого-нибудь google closure compiler - который вроде часто обновляется, нужно его перевшивать в UnGUI, для портабельности не вижу проблем хранить всё просто в одном каталоге/архиве, плюс получается некий комбайн включающий в себя сторонний софт, хотя для личного пользования почему бы и нет. + +>Может подкинешь идеек? +Сначала нужно определиться, где сохранять последние параметры, в файле загруженного конфига или в отдельном/главном конфиге, +почему-то мне кажется что в отдельном не нужно - потенциально приведет к багам/конфликтам разных версий конфигов, увеличению кол-ва файлов. Вижу простое решение, не знаю как насчёт красоты - если есть ключ default_n, то нет проблем добавить при первой выгрузке конфига/закрытия программы ключ last_saved_n, +при загрузки конфига проверяем наличие/считываем last_saved_n, если отсутствует или пустая строка, используем default_n, при сбросе на дефолт просто удаляем во всех секциях last_saved_n, сохраняем на хард и перезагружаем этот конфиг. Но что-то я подумал, что это элементарно и ты спрашивал про идеи другого рода. + +_____________________ + +Аноним (Microsoft Windows 7: Chromium based) 30/12/17 Суб 03:28:59 №2221618 +>>2221472 +>подумать над приоритетом встроенного конфига и внешнего. + +Думаю внутренний вообще не должен приниматься в работу, полезность этого вижу лишь в плане дистрибуции, при запуске просто проверям наличие одноименного конфига на харде, если нет, то выгружаем на хард и далее уже работаем только с ним. Алсо можно ещё и документацию зашить в каком-либо удобочитаемом виде. Я зашивал небольшой гайд в виде rtf, предварительно красиво его отформатировав - с цветами, параграфами, гиперссылками, и отображал в Rich Edit контроле. Можно просто зашить html и окрывать в браузере как многие делают по F1. +Пока нет времени серьёзно что-нибудь законфигурировать и потестить - тут и НГ и ДР - куча родственников с другого города, шумных детей с ними, а у меня тут еще своих срочных дел поднакопилось, но я слежу за разработкой, спасибо Анон! + diff --git a/UnGUI/CommandLineToArgvA.c b/UnGUI/CommandLineToArgvA.c new file mode 100644 index 0000000..d080af5 --- /dev/null +++ b/UnGUI/CommandLineToArgvA.c @@ -0,0 +1,31 @@ +/* (С) AHOHNMYC, 2017 + * part of Universal GUI project + * CC-NC-SA + */ + +#include + +#define MAX_PARAM_COUNT 256 +LPCSTR * CommandLineToArgvA (LPSTR lpCmdLine, int *pNumArgs) { + DWORD i = 0, lpCmdLineLen = strlen(lpCmdLine); + LPCSTR * argv = LocalAlloc(LMEM_FIXED|LMEM_ZEROINIT, MAX_PARAM_COUNT*sizeof(LPCSTR)); + BOOL quoteFlag = FALSE; + + argv[0] = lpCmdLine; + + CHAR stopSymbol = (lpCmdLine[0] == '"') ? '"' : ' '; + while ( lpCmdLine[++i] != stopSymbol ); + + if (stopSymbol == '"') argv[0]++, lpCmdLine[i] = '\0'; + + *pNumArgs = 1; + for (; i < lpCmdLineLen; i++) { + if (lpCmdLine[i] == '"') + lpCmdLine[i] = '\0', quoteFlag = !quoteFlag; + if (lpCmdLine[i] == ' ' && !quoteFlag) + lpCmdLine[i] = '\0'; + if (lpCmdLine[i-1] == '\0' && lpCmdLine[i] != '\0') + argv[++*pNumArgs-1] = lpCmdLine+i; + } + return argv; +} diff --git a/UnGUI/UnGUI.c b/UnGUI/UnGUI.c new file mode 100644 index 0000000..c400f8c --- /dev/null +++ b/UnGUI/UnGUI.c @@ -0,0 +1,722 @@ +#include +#include +#include "./UnGUI.h" +#include "./ini/src/ini.c" +#include "./CommandLineToArgvA.c" + +#define PROG_TITLE "Universal GUI" +#define PROG_TITLE_SHORT "UnGUI" +#define PROG_VERSION "v0.0.1.41" +#define PROG_HELP_TEXT "Universal GUI v0.0.1.41\n\ +(C) AHOHNMYC, 2017\n\ +https://GitHub.com/AHOHNMYC\n\n\ +-c \t\tload Config from file\n\ +-t <0|1>\t\ton Top\n\ +-l \tLanguage\n\ +-e \t\tEmbed config in resources" + +HWND hwnd, nameEdit, keyEdit, exePathEdit, startBtn, cmdLineEdit; +ini_t* ini_file; +accepted_params cmd = {0}; +runtime_options runt_opt = {0}; + +LPCSTR ini_check_section(ini_t *ini, LPCSTR section) { + LPSTR current_section = ""; + LPSTR p = ini->data; + + if (*p == '\0') p = next(ini, p); + + while (p < ini->end) { + if (*p == '[') { + /* Handle section */ + current_section = p + 1; + if (!strcmpi(section, current_section)) { + return p; + } + } else { + next(ini, p); + } + p = next(ini, p); + } + return NULL; +} + +BOOL strcmpi_mul (LPCSTR str, int count, ...) { + LPSTR str2; + + if (str == NULL) + return FALSE; + + va_list args; + va_start(args, count); + while (count--) { + str2 = va_arg(args, LPSTR); + if (str2 == NULL) { + va_end(args); + return FALSE; + } + if ( !strcmpi(str, str2) ) { + va_end(args); + return TRUE; + } + } + va_end(args); + return FALSE; +} + +BOOL ini_is_true (ini_t *ini, LPCSTR section, LPCSTR key) { + return strcmpi_mul(ini_get(ini, section, key), 2, "1", "true"); +} + +void exitProgram(int retCode) { + ShowWindow(hwnd, SW_HIDE); + ExitProcess(retCode); +} + +/* economy on stack pushing */ +HWND cw_ex(DWORD exStyle, LPSTR classw, LPSTR name, DWORD style, int x, int y, int w, int h) { + return CreateWindowExA(exStyle, classw, name, style, x, y, w, h, hwnd, NULL, NULL, NULL); +} +HWND cw(LPSTR classw, LPSTR name, DWORD style, int x, int y, int w, int h) { + return cw_ex(0, classw, name, style, x, y, w, h); +} + + + +HWND CreateToolTip(HWND hwndTool, HWND hDlg, PTSTR pszText) { + // Create the tooltip. g_hInst is the global instance handle. + HWND hwndTip = CreateWindowEx(0, TOOLTIPS_CLASS, NULL, + WS_POPUP | TTS_BALLOON | TTS_NOFADE, + CW_USEDEFAULT, CW_USEDEFAULT, + CW_USEDEFAULT, CW_USEDEFAULT, + hDlg, NULL, + NULL, NULL); + + if (!hwndTool || !hwndTip) { + return (HWND)NULL; + } + + // Associate the tooltip with the tool. + TOOLINFO toolInfo = {0}; + toolInfo.cbSize = sizeof(toolInfo); + toolInfo.hwnd = hDlg; + toolInfo.uFlags = TTF_IDISHWND | TTF_SUBCLASS; + toolInfo.uId = (UINT_PTR)hwndTool; + toolInfo.lpszText = pszText; + SendMessage(hwndTip, TTM_ADDTOOL, 0, (LPARAM)&toolInfo); + + +// Remove delays in displaying + SendMessage(hwndTip, TTM_SETDELAYTIME, TTDT_INITIAL, 0); + SendMessage(hwndTip, TTM_SETDELAYTIME, TTDT_RESHOW, 0); +// Hide after 15 seconds. Fuck yeah + SendMessage(hwndTip, TTM_SETDELAYTIME, TTDT_AUTOPOP, 15000); + + + return hwndTip; +} + +// also used in __main +DWORD windStyles = WS_CAPTION|WS_SYSMENU; + +typedef struct { int x,y,w,h; } WPOS; +WPOS getCoordsForCenter(int width, int height) { + int x = width, y = height, dx, dy; + RECT rectWnd = {0,0,x,y}; + + AdjustWindowRect(&rectWnd, windStyles, FALSE); + + x = rectWnd.right - rectWnd.left; + y = rectWnd.bottom - rectWnd.top; + dx = (GetSystemMetrics(SM_CXSCREEN) - rectWnd.right )/2; + dy = (GetSystemMetrics(SM_CYSCREEN) - rectWnd.bottom)/2; + + WPOS w = {dx,dy,x,y}; + return w; +} + + +COORD coords = {20, 0}; + + +#define TYPE_CHECKBOX 0x01 +#define TYPE_EDIT 0x02 +#define TYPE_STATIC 0x04 +#define TYPE_RADIO 0x08 +#define TYPE_BUTTON 0x10 +#define TYPE_COMBOBOX 0x20 + +void initHandles(groupOfParamsEntry* parEn, DWORD groupID) { + char type[10]; + DWORD style = WS_CHILD|WS_VISIBLE|WS_TABSTOP; + DWORD exStyle = 0; + long typeFlag = TYPE_CHECKBOX; + + if ( !strcmpi(parEn->type, "checkbox") ) { + typeFlag = TYPE_CHECKBOX; + } else if ( !strcmpi(parEn->type, "edit") ) { + typeFlag = TYPE_EDIT; + } else if ( !strcmpi(parEn->type, "static") ) { + typeFlag = TYPE_STATIC; + } else if ( !strcmpi(parEn->type, "radio") ) { + typeFlag = TYPE_RADIO; + } else if ( !strcmpi(parEn->type, "button") ) { + typeFlag = TYPE_BUTTON; + } else if ( !strcmpi(parEn->type, "combobox") ) { + typeFlag = TYPE_COMBOBOX; + } + + strcpy(type, parEn->type); + POINT wh = {180, 15}; + + switch (typeFlag) { + case TYPE_CHECKBOX: + strcpy(type, "button"); + style |= BS_AUTOCHECKBOX; + break; + case TYPE_RADIO: + strcpy(type, "button"); + style |= BS_RADIOBUTTON; + break; + case TYPE_EDIT: + style |= WS_BORDER|ES_AUTOHSCROLL; + exStyle |= WS_EX_ACCEPTFILES; + wh.y += 2; + break; + case TYPE_STATIC: + style -= WS_TABSTOP; + break; + case TYPE_BUTTON: + break; + case TYPE_COMBOBOX: + strcpy(type, "combobox"); + style |= CBS_DROPDOWNLIST; + break; + } + + + if (parEn->key_count > 1) { + coords.X = 20; + + parEn->group_box_hndl = cw("button", parEn->title, WS_CHILD|WS_VISIBLE|BS_GROUPBOX, coords.X-10, coords.Y, 200, + (typeFlag==TYPE_EDIT||typeFlag==TYPE_COMBOBOX?19:15) * (typeFlag==TYPE_COMBOBOX?1:parEn->key_count) + 30 - (parEn->title[0] == '\0' ? 5:0) ) + ; + + if (parEn->hint[0] != '\0') { + #define SQUARE_HELP_BUTTON_SIZE 17 + parEn->group_box_hint_hndls[0] = cw("button", "?", WS_CHILD|WS_VISIBLE|BS_FLAT, 200, coords.Y, SQUARE_HELP_BUTTON_SIZE, SQUARE_HELP_BUTTON_SIZE); + parEn->group_box_hint_hndls[1] = CreateToolTip(parEn->group_box_hint_hndls[0], hwnd, parEn->hint); + } + + coords.Y += (parEn->title[0] == '\0') ? 15 : 20; + } else { +// coords.X = 10; + coords.Y += 10; + } + + DWORD i=0; + if (typeFlag != TYPE_COMBOBOX) { + for (; i < parEn->key_count; i++) { + parEn->handles[i] = CreateWindowExA(exStyle, type, + (*parEn->values[i] == '\0' && typeFlag != TYPE_EDIT) + ? parEn->keys[i] + : parEn->values[i], + style, coords.X, coords.Y, wh.x, wh.y, + hwnd, (HMENU)groupID, NULL, NULL); + + SendMessage(parEn->handles[i], BM_SETCHECK, parEn->defaults[i], 0); + + if (*parEn->hints[i] != '\0') + parEn->hint_handles[i] = CreateToolTip(parEn->handles[i], hwnd, parEn->hints[i]); + + if ( typeFlag == TYPE_EDIT ) + coords.Y += 4; + coords.Y += 15; + } + } else { + parEn->handles[0] = CreateWindowExA(exStyle, type, "", style, coords.X, coords.Y, wh.x, wh.y, hwnd, (HMENU)groupID, NULL, NULL); + SendMessage(parEn->handles[0], CB_SETCURSEL, 0, 0); + + BOOL defaultChanged = FALSE; + for (; i < parEn->key_count; i++) { + SendMessage(parEn->handles[0], CB_ADDSTRING, 0, (LPARAM)(*parEn->values[i]!='\0' ? parEn->values[i] : parEn->keys[i])); + if (!defaultChanged && parEn->defaults[i]) { + SendMessage(parEn->handles[0], CB_SETCURSEL, i, 0); + defaultChanged = TRUE; + } + } + + coords.Y += 15; + } + + +// huita kakaya-to vyhodit +// if (typeFlag == TYPE_COMBOBOX) { +// DlgDirListComboBox(hwnd, "windows", GetDlgCtrlID(parEn->handles[i]), 0, DDL_ARCHIVE); +// } + + + + if ( parEn->key_count > 1 || typeFlag!=TYPE_STATIC ) { + coords.Y += 8; + } + + MoveWindow(exePathEdit, 10, coords.Y+10, 200, 17, TRUE); + MoveWindow(cmdLineEdit, 10, coords.Y+29, 200, 17, TRUE); + MoveWindow(startBtn, 10, coords.Y+48+5, 200, 30, TRUE); +// MoveWindow(startBtn, 20, coords.Y+48+5, 180, 30, TRUE); + + WPOS w = getCoordsForCenter(220, coords.Y+48+5+30+10); + SetWindowPos(hwnd, 0, w.x, w.y, w.w, w.h, SWP_NOZORDER); +} + +LPCSTR zero_str = ""; +LPCSTR ini_get_def (ini_t *ini, LPCSTR section, LPCSTR key, LPCSTR fallback) { + LPCSTR tmp = ini_get(ini, section, key); + return tmp==NULL? fallback : tmp; +} +LPCSTR ini_get_def_zero_str (ini_t *ini, LPCSTR section, LPCSTR key) { + return ini_get_def(ini, section, key, zero_str); +} + +groupOfParamsEntry* initGroup(LPSTR group_section_name) { + groupOfParamsEntry* grEn; + grEn = LocalAlloc(LMEM_FIXED|LMEM_ZEROINIT, sizeof(groupOfParamsEntry)); + + strcpy(grEn-> title, ini_get_def_zero_str(ini_file, group_section_name, "title")); + strcpy(grEn-> hint, ini_get_def_zero_str(ini_file, group_section_name, "hint")); + strcpy(grEn-> type, ini_get_def (ini_file, group_section_name, "type", "checkbox")); + strcpy(grEn->group_prefix, ini_get_def_zero_str(ini_file, group_section_name, "group_prefix")); + strcpy(grEn-> prefix, ini_get_def (ini_file, group_section_name, "prefix", " ")); + +// LPSTR param_value_key = "value_1\0\0\0"; +// LPSTR param_key_key = "key_1\0\0\0"; +// LPSTR param_default_key = "default_1\0\0\0"; +// LPSTR param_hint_key = "hint_1\0\0\0"; + + char param_value_key[20] = "value_1"; + char param_key_key[20] = "key_1"; + char param_default_key[20] = "default_1"; + char param_hint_key[20] = "hint_1"; + int i = 1; + while ( ( ini_get(ini_file, group_section_name, param_key_key) != NULL + || (strcmpi_mul(grEn->type, 3, "edit", "static", "combobox") && ini_get(ini_file, group_section_name, param_value_key) != NULL) + ) && i <= 256 ) { + strcpy(grEn->values[i-1], ini_get_def_zero_str(ini_file, group_section_name, param_value_key)); + strcpy(grEn-> keys[i-1], ini_get_def_zero_str(ini_file, group_section_name, param_key_key)); + strcpy(grEn-> hints[i-1], ini_get_def_zero_str(ini_file, group_section_name, param_hint_key)); + grEn->defaults[i-1] = ini_is_true(ini_file, group_section_name, param_default_key); + + i++; + sprintf(param_value_key, "value_%d", i); + sprintf(param_key_key, "key_%d", i); + sprintf(param_default_key, "default_%d", i); + sprintf(param_hint_key, "hint_%d", i); + }; + grEn->key_count = i-1; + + coords.Y += 5; + +/* explosive arithmetic + * "param_group_1" + * +12 => ^ + * -1 to sync numbers with group order in parsed_config->groups, which starts from 0 + */ initHandles(grEn, atoi(group_section_name+12)-1); + + return grEn; +} + +parsed_config* config; + +char localeSection[10] = ""; +void determineLocaleSection() { + if (localeSection[0] != '\0') return; // we may set locale from cmd via -l switch + + char locAbbr[4], probabSection[10]; + if ( GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SABBREVCTRYNAME, locAbbr, sizeof(locAbbr)) ) { + sprintf(probabSection, "lang_%s", locAbbr); + if ( ini_check_section(ini_file, probabSection) ) { + strcpy(localeSection, probabSection); + return; + } + } + + sprintf(probabSection, "lang_%d", (int)GetSystemDefaultLCID()); + if ( ini_check_section(ini_file, probabSection) ) { + strcpy(localeSection, probabSection); + return; + } + + if ( ini_check_section(ini_file, "lang") ) { + strcpy(localeSection, "lang"); + return; + } +} + +LPCSTR getLocalizedStr(LPCSTR key, LPCSTR english_fallback) { + return ini_get_def(ini_file, localeSection, key, english_fallback); +} + +void loadLocale() { + determineLocaleSection(); + CreateToolTip(exePathEdit, hwnd, (PTSTR)getLocalizedStr("tooltip_edit_exePath", "Path to executable file")); + CreateToolTip(cmdLineEdit, hwnd, (PTSTR)getLocalizedStr("tooltip_edit_cmdLine", "Generated command line")); +// SetWindowText(startBtn, getLocalizedStr("btn_start", "&Start")); + char t[30]; sprintf(t, ">>> %s <<<", getLocalizedStr("btn_start", "&Start")); + SetWindowText(startBtn, t); + CreateToolTip(startBtn, hwnd, (PTSTR)getLocalizedStr("tooltip_btn_start", "Press here")); +} + +void updateCmdline() { + TBYTE curGroup, curKey; + LPSTR cmdline = LocalAlloc(LMEM_FIXED|LMEM_ZEROINIT, 0x10000); // 65kb for cmdline — norm + LPSTR cmdlineGroup = LocalAlloc(LMEM_FIXED|LMEM_ZEROINIT, 0x1000); // 4kb for every group + + char buf[256]; // Think about size more + for (curGroup=0; curGroup < config->group_count; curGroup++) { + cmdlineGroup[0] = '\0'; + + if ( !strcmpi(config->groups[curGroup]->type, "combobox") ) + sprintf(cmdlineGroup, "%s%s%s", + cmdlineGroup, + config->groups[curGroup]->prefix, + config->groups[curGroup]->keys[ SendMessage(config->groups[curGroup]->handles[0], CB_GETCURSEL, 0, 0) ] ); + else + for (curKey=0; curKey < config->groups[curGroup]->key_count; curKey++) + if ( !strcmpi(config->groups[curGroup]->type, "edit") ) { + GetWindowText(config->groups[curGroup]->handles[curKey], buf, sizeof(buf) ); + if (buf[0] != '\0') + sprintf(cmdlineGroup, "%s%s%s", cmdlineGroup, config->groups[curGroup]->prefix, buf); + } else if ( BST_CHECKED == SendMessage(config->groups[curGroup]->handles[curKey], BM_GETCHECK, 0, 0) ) + sprintf(cmdlineGroup, "%s%s%s", + cmdlineGroup, + config->groups[curGroup]->prefix, + config->groups[curGroup]->keys[curKey]); + + if (cmdlineGroup[0] != '\0') + sprintf(cmdline, "%s%s%s", cmdline, config->groups[curGroup]->group_prefix, cmdlineGroup); + } + SetWindowText(cmdLineEdit, cmdline+(cmdline[0]==' '?1:0) ); // +1 to filter firsst space + + LocalFree(cmdlineGroup); + LocalFree(cmdline); +} + +// Returns HLOCAL (pointer to local memory) that contains config data +// Also returns config size in DWORD size variable +HLOCAL getConfig(LPDWORD size) { + LPCSTR fileName = cmd.config? cmd.config : "config.ini"; + + OFSTRUCT os; + HFILE file = OpenFile(fileName, &os, OF_READ); + + if (file != HFILE_ERROR) { + DWORD sizeLo = GetFileSize((HANDLE)file, NULL), sizeHi = 0; + HLOCAL buf = LocalAlloc(LMEM_FIXED|LMEM_ZEROINIT, sizeLo+1); + ReadFile((HANDLE)file, buf, sizeLo, &sizeHi, NULL); + + runt_opt.is_config_in_resources = FALSE; + runt_opt.config_path = fileName; + *size = sizeLo; + return buf; + } + + HRSRC res = FindResource(NULL, "DEFAULT", "CONFIG"); + if (res != NULL) { + runt_opt.is_config_in_resources = TRUE; + *size = SizeofResource(NULL, res); + return (HLOCAL)LoadResource(NULL, res); + } + + MessageBox(0, "Config not found.\nEven in resources.\nHonestly, it's strange.\nTry renaming to \"config.ini\".\n\nWe are closing.", PROG_TITLE, MB_ICONWARNING); + ExitProcess(1); +} + +void loadConfig() { + config = LocalAlloc(LMEM_FIXED|LMEM_ZEROINIT, sizeof(parsed_config)); + + DWORD size; + HLOCAL file = getConfig(&size); + ini_file = ini_load(file, size); + + if ( cmd.on_top == 2 || ( cmd.on_top != 1 && ini_is_true(ini_file, "options", "on_top") ) ) { + runt_opt.is_on_top = TRUE; + SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE); + } + + config->do_exit_after_launch = ini_is_true(ini_file, "options", "exit_after_launch"); + + char title_result[256]; + LPCSTR title = ini_get_def_zero_str(ini_file, "options", "title"); + if (*title!='\0') sprintf(title_result, "%s - %s", PROG_TITLE_SHORT, title); + else sprintf(title_result, "%s %s", PROG_TITLE, PROG_VERSION); + SetWindowText(hwnd, title_result); + + SetWindowText(exePathEdit, ini_get_def_zero_str(ini_file, "options", "command")); + + char group_section_name[20] = "param_group_1\0\0\0"; + int i = 1; + while ( ini_check_section(ini_file, group_section_name) != NULL && i <= 256 ) { + config->groups[i-1] = initGroup(group_section_name); + sprintf(group_section_name, "param_group_%d", ++i); + }; + config->group_count = i-1; + + loadLocale(); +// ini_free(ini_file); + + updateCmdline(); +} + +BOOL UPXcheckAndUnpack(LPCSTR fileName) { + LPOFSTRUCT os = LocalAlloc(LMEM_ZEROINIT, sizeof(OFSTRUCT)); + HFILE file = OpenFile(fileName, os, OF_READ); + DWORD size1 = 0; + LPSTR buf = LocalAlloc(LMEM_FIXED|LMEM_ZEROINIT, 4); + SetFilePointer((HANDLE)file, 0x1F5, NULL, FILE_BEGIN); + ReadFile((HANDLE)file, buf, 3, &size1, NULL); + CloseHandle((HANDLE)file); + + if ( !strcmp(buf, "UPX") ) { +// MessageBox(0, "File packed with UPX", PROG_TITLE, MB_ICONINFORMATION); + if ( SearchPath(".", "upx", ".exe", 0, NULL, NULL) ) { +// MessageBox(0, "UPX found", PROG_TITLE, MB_ICONINFORMATION); + + STARTUPINFO si = {0}; + si.cb = sizeof(STARTUPINFO); + si.dwFlags = STARTF_USESHOWWINDOW; + si.wShowWindow = SW_HIDE; + + PROCESS_INFORMATION pi; + + CHAR params[MAX_PATH+5]; + sprintf(params, " -d %s", fileName); + + CreateProcess("upx.exe", params, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi); + + while ( WaitForSingleObject(pi.hProcess, 500) == WAIT_TIMEOUT ); + + DWORD exitCode; + GetExitCodeProcess(pi.hProcess, &exitCode); + if (!exitCode) { +// MessageBox(0, "Successfully unpacked", PROG_TITLE, MB_ICONINFORMATION); + } else { + sprintf(params, "Cannot unpack. Try \"upx -d %s\" manually", fileName); + MessageBox(0, params, PROG_TITLE, MB_ICONERROR); + return FALSE; + } + } else { + MessageBox(0, "UPX not found. Goto\nhttps://upx.github.io\nand unpack \"upx.exe\" here", PROG_TITLE, MB_ICONERROR); + return FALSE; + } + } else { + MessageBox(0, "File NOT packed with UPX", PROG_TITLE, MB_ICONINFORMATION); + } + return TRUE; +} + +BOOL resourceReplace(HANDLE exeOpenedToUpdate, LPCSTR resFile, LPCSTR resName) { + OFSTRUCT os; + HFILE file = OpenFile(resFile, &os, OF_READ); + DWORD size = GetFileSize((HANDLE)file, NULL), size1 = 0; + LPSTR buf = LocalAlloc(LMEM_FIXED|LMEM_ZEROINIT, size+1); + ReadFile((HANDLE)file, buf, size, &size1, NULL); + +// return UpdateResource(exeOpenedToUpdate, "CONFIG", resName, 0, buf, size+1); + return UpdateResource(exeOpenedToUpdate, "CONFIG", resName, 1033, buf, size+1); // 1033 — 'coz windres by default makes resources in English. We have to change it to 0 +} + +void resourceEmbed(LPCSTR fileName, LPCSTR fileResName) { + DeleteFile("UnGUI.tmp"); + MoveFile(fileName, "UnGUI.tmp"); + CopyFile("UnGUI.tmp", fileName, TRUE); + + if ( !UPXcheckAndUnpack(fileName) ) + goto clean_after; + + HANDLE exeOpenedToUpdate = BeginUpdateResource(fileName, FALSE); + resourceReplace(exeOpenedToUpdate, fileResName, "DEFAULT"); + EndUpdateResource(exeOpenedToUpdate, FALSE); + + if ( SearchPath(".", "upx", ".exe", 0, NULL, NULL) ) { + ShellExecute(NULL, NULL, "upx.exe", fileName, NULL, SW_HIDE); + } else { + MessageBox(0, "UPX not found. Goto\nhttps://upx.github.io\nand unpack \"upx.exe\" here\nto reduce size", PROG_TITLE, MB_ICONINFORMATION); + } + + MessageBox(0, "Con-fug have beeen successfully\nembedded as default.", PROG_TITLE, MB_ICONINFORMATION); + + CHAR comspec[MAX_PATH]; +clean_after: + ExpandEnvironmentStrings("%ComSpec%", comspec, MAX_PATH); + ShellExecute(NULL, NULL, comspec, "/c ping 127.0.0.1 -n 2 & del UnGUI.tmp", NULL, SW_HIDE); + ExitProcess(0); +} + +int CALLBACK SetChildFont(HWND child, LPARAM font) { + SendMessageA(child, WM_SETFONT, font, (LPARAM)TRUE); + return 1; +} + +void startExe() { + char exe[256]; + #define PARAM_SIZE 0x10000 + LPSTR params = LocalAlloc(LMEM_FIXED|LMEM_ZEROINIT, PARAM_SIZE); // 65kb for cmdline — norm + GetWindowText(exePathEdit, exe, sizeof(exe) ); + GetWindowText(cmdLineEdit, params, PARAM_SIZE); + + LPSTR message = LocalAlloc(LMEM_FIXED|LMEM_ZEROINIT, 0x100); + DWORD result = ShellExecute(NULL, NULL, exe, params, NULL, SW_SHOWDEFAULT); + switch (result) { + case ERROR_FILE_NOT_FOUND: + sprintf(message, getLocalizedStr("error_no_file", "File \"%s\" not found"), exe); + break; + case ERROR_PATH_NOT_FOUND: + sprintf(message, getLocalizedStr("error_no_path", "Path not found")); + break; + default: + if (result <= 32) + sprintf(message, getLocalizedStr("error_unknown", "Unknown error 0x%08X"), result); + } + if (message[0] != '\0') + MessageBox(hwnd, message, getLocalizedStr("error", "Error"), MB_ICONERROR|(runt_opt.is_on_top?MB_TOPMOST:0) ); + else + if (config->do_exit_after_launch) + exitProgram(0); + + LocalFree(message); + LocalFree(params); +} + +LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) { + switch(Message) { + case WM_COMMAND: { + if ( GetWindowLong((HWND)lParam, GWL_STYLE) & BS_RADIOBUTTON ) { + int grpID = GetWindowLong((HWND)lParam, GWL_ID); + int i = config->groups[grpID]->key_count; + while (i--) + SendMessage(config->groups[grpID]->handles[i], BM_SETCHECK, BST_UNCHECKED, 0); + SendMessage((HWND)lParam, BM_SETCHECK, BST_CHECKED, 0); + } + + if (lParam == (LPARAM)startBtn) { + startExe(); + break; + } + // Change text, but not on main edit window, to not bit userentered params + // And not after click on main "Start" button + if ( ( HIWORD(wParam) == EN_UPDATE && !( (HWND)lParam == exePathEdit) && !( (HWND)lParam == cmdLineEdit) ) + || (HIWORD(wParam) == BN_CLICKED && !( (HWND)lParam == startBtn) ) + || (HIWORD(wParam) == CBN_SELCHANGE) ) updateCmdline(); + break; + } + case WM_CLOSE: + case WM_DESTROY: + case WM_QUIT: { + ExitProcess(0); + } + case WM_ACTIVATE: { + SetFocus(startBtn); + break; + } + case WM_SHOWWINDOW: { + EnumChildWindows(hwnd, (WNDENUMPROC)SetChildFont, (LPARAM)GetStockObject(DEFAULT_GUI_FONT)); + break; + } + default: + return DefWindowProcA(hwnd, Message, wParam, lParam); + } + return 0; +} + +void showHelpWindow() { + MessageBox(0, PROG_HELP_TEXT, PROG_TITLE, MB_ICONINFORMATION); + ExitProcess(0); +} + +void analyzeCmdParams () { + int argc; + LPCSTR* argv = CommandLineToArgvA(GetCommandLine(), &argc); + + char ch; + int curArgv = 0; + cmd.optional_count = 0; + + while (++curArgv < argc) + if ( argv[curArgv][0] == '-' && (ch = argv[curArgv][1]) ) { + if (argv[curArgv][2] != '\0') { + showHelpWindow(); + continue; + } + switch (ch) { + case 'c': { + cmd.config = argv[++curArgv]; + break; + } + case 't': { + cmd.on_top = 1+atoi(argv[++curArgv]); + break; + } + case 'l': { +// cmd.lang = argv[++curArgv]; + sprintf(localeSection, "lang_%s", argv[++curArgv]); + break; + } + case 'a': { + cmd.autoclose = atoi(argv[++curArgv]); + break; + } + case 'e': { + resourceEmbed(argv[0], argv[++curArgv]); + break; + } + default: { + showHelpWindow(); + } + } + } else { + if ( strcmpi_mul(argv[curArgv], 3, "/?", "/h", "/help") ) { + showHelpWindow(); + continue; + } + +// char t[30]; sprintf(t, "%s — нинужно. Нахер ты это сюда припёр?\nУпирай обратно!", argv[curArgv]); +// MessageBox(0, t, "", MB_ICONEXCLAMATION); + cmd.optionals[cmd.optional_count] = argv[curArgv]; + cmd.optional_count++; + } +} + +void __main() { +// InitCommonControls(); + + WNDCLASSEXA wc = {0}; + MSG msg; + + wc.cbSize = sizeof(WNDCLASSEX); + wc.lpfnWndProc = WndProc; + wc.hbrBackground = (HBRUSH)COLOR_WINDOW; + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.lpszClassName = "WindowClass"; + RegisterClassExA(&wc); + + hwnd = cw("WindowClass", "", windStyles, 0,0,0,0); + exePathEdit = cw_ex(WS_EX_ACCEPTFILES, "edit", "", WS_CHILD|WS_VISIBLE|WS_TABSTOP|WS_BORDER|ES_AUTOHSCROLL, 0,0,0,0); + cmdLineEdit = cw_ex(WS_EX_ACCEPTFILES, "edit", "", WS_CHILD|WS_VISIBLE|WS_TABSTOP|WS_BORDER|ES_AUTOHSCROLL, 0,0,0,0); + startBtn = cw("button", "", WS_CHILD|WS_VISIBLE|WS_TABSTOP|BS_DEFPUSHBUTTON/*|BS_DEFCOMMANDLINK*/, 0,0,0,0); + + analyzeCmdParams(); + loadConfig(); + + ShowWindow(hwnd, SW_SHOW); + + while(GetMessageA(&msg, NULL, 0, 0) > 0) { + if (msg.message == WM_KEYDOWN && msg.wParam == VK_ESCAPE) { + exitProgram(0); + } + + if(IsDialogMessageA(hwnd, &msg)) continue; + TranslateMessage(&msg); + DispatchMessageA(&msg); + } +} diff --git a/UnGUI/UnGUI.dev b/UnGUI/UnGUI.dev new file mode 100644 index 0000000..e6fcb11 --- /dev/null +++ b/UnGUI/UnGUI.dev @@ -0,0 +1,81 @@ +[Project] +Type=0 +Ver=2 +Linker=-l kernel32_@@_-l user32_@@_-l gdi32_@@_-l msvcrt_@@_-l comctl32_@@_-l shell32_@@_-e ___main_@@_-l mingwex_@@_-l mingw32_@@_-l crtdll_@@_ +CompilerSet=3 +CompilerSettings=000aa0d1a0111000000111000 +UnitCount=3 +UseCustomMakefile=0 +CustomMakefile=Makefile.win +FileName=UnGUI.dev +Name= +ObjFiles= +Includes= +Libs= +PrivateResource=UnGUI_private.rc +ResourceIncludes= +MakeIncludes=upx.mak +Compiler= +CppCompiler= +IsCpp=0 +Icon= +ExeOutput= +ObjectOutput= +LogOutput= +LogOutputEnabled=0 +OverrideOutput=0 +OverrideOutputName=UnGUI.exe +HostApplication= +CommandLine= +Folders= +IncludeVersionInfo=1 +SupportXPThemes=1 + +[Unit1] +FileName=UnGUI.c +CompileCpp=0 +Folder= +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit2] +FileName=UnGUI.h +CompileCpp=0 +Folder= +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[VersionInfo] +Major=0 +Minor=0 +Release=1 +Build=42 +LanguageID=1033 +CharsetID=1252 +CompanyName=AHOHNMYC +FileVersion=0.0.1.42 +FileDescription=Just executable file. Note: prepare your anus^W config! +InternalName= +LegalCopyright=(c) AHOHNMYC, 2017.\nDistributed without any possible express or implied warranty.\nRisk and possible damages on your conscience.\nYou were warned.\nOnly for non-commertial use. +LegalTrademarks= +OriginalFilename=UnGUI.exe +ProductName=Universal GUI +ProductVersion=30.12.2017 +AutoIncBuildNr=0 +SyncProduct=0 + +[Unit3] +FileName=embedded_config.rc +Folder=Resources +Compile=1 +Link=0 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + diff --git a/UnGUI/UnGUI.exe b/UnGUI/UnGUI.exe new file mode 100644 index 0000000..f928024 Binary files /dev/null and b/UnGUI/UnGUI.exe differ diff --git a/UnGUI/UnGUI.h b/UnGUI/UnGUI.h new file mode 100644 index 0000000..3d95835 --- /dev/null +++ b/UnGUI/UnGUI.h @@ -0,0 +1,49 @@ +#define MAX_PARAMS_COUNT 32 +typedef struct { + CHAR title[256]; + CHAR hint[256]; + CHAR type[256]; + CHAR group_prefix[256]; + CHAR prefix[256]; + + HWND group_box_hndl; + HWND group_box_hint_hndls[2]; + + DWORD key_count; + CHAR values[MAX_PARAMS_COUNT][256]; + CHAR keys[MAX_PARAMS_COUNT][256]; + BOOL defaults[MAX_PARAMS_COUNT]; + CHAR hints[MAX_PARAMS_COUNT][256]; + HWND handles[MAX_PARAMS_COUNT]; + HWND hint_handles[MAX_PARAMS_COUNT]; +} groupOfParamsEntry; + +// uses in options.options +#define OPTION_ON_TOP 0x00000001 +#define OPTION_SAVE_ON_EXIT 0x00000002 +typedef struct { +// CHAR command[256]; // path to exec file + BOOL do_exit_after_launch; + DWORD group_count; // count of groups of options + groupOfParamsEntry* groups[256]; // groups of options +} parsed_config; + + +typedef struct { + LPCSTR config; +// LPCSTR lang; + DWORD on_top; + DWORD autoclose; + DWORD optional_count; + LPCSTR optionals[256]; +} accepted_params; + +typedef struct { + BOOL is_on_top; +// LPCSTR curr_lang; + + BOOL is_config_in_resources; + LPCSTR config_path; +} runtime_options; + + diff --git a/UnGUI/UnGUI.layout b/UnGUI/UnGUI.layout new file mode 100644 index 0000000..56082be --- /dev/null +++ b/UnGUI/UnGUI.layout @@ -0,0 +1,15 @@ +[Editors] +Order=1,0 +Focused=0 + +[Editor_0] +CursorCol=1 +CursorRow=578 +TopLine=547 +LeftChar=1 + +[Editor_1] +CursorCol=7 +CursorRow=6 +TopLine=1 +LeftChar=1 diff --git a/UnGUI/config_to_embed.ini b/UnGUI/config_to_embed.ini new file mode 100644 index 0000000..a27f54a --- /dev/null +++ b/UnGUI/config_to_embed.ini @@ -0,0 +1,160 @@ +; Demo version^W file + +; Portion of default OS шindoшs hotkeys ^___^ +; Tab — next element +; Shift+Tab — previous element +; Up/Down — previous/next element on radiobutton +; Alt+ArrowDown — expand dropdown combobox + + +[options] +title =Wishmaster 2000 +command =hui.exe +on_top =true +exit_after_launch=true + + +[param_group_1] +title =Checkbox group +hint =This is Checkbox group hint +type =checkbox +group_prefix=-gp +; space-starting/ending strings have to be quoted +prefix =" -p " + +value_1 =Checkbox #1 +key_1 =cb11 +default_1 =true +hint_1 =This option add "cb11" to resulting cmdline + +key_2 =cb12 +hint_2 =This option add "cb12" to resulting cmdline + +key_3 =cb13 + + +[param_group_2] +; If "type" not set, we think that you thought about checkbox +key_1 =cb21 +hint_1 =This is standalone checkbox + + +[param_group_3] +title =Radio group +hint =This is Radio group hint. This group switches "rb31" and "rb32" +type =radio + +value_1 =Radio option #1 +key_1 =rb31 +hint_1 =This option add "rb31" to resulting cmdline + +key_2 =rb32 +default_2 =1 + + +[param_group_4] +title =Static group +type =static +hint =There is no spoon + +value_1 =Line with wise text +value_2 =Second line + +hint_1 =!!! S-P-O-O-N !!! +hint_2 =Hints doesn't work on Static + + +; order isn't needed. Author is not perfectionist. I'm depressionist. +[param_group_6] +type =static +key_1 =This is group without +key_2 =Title and group hint + + +[param_group_5] +title =Will not be shown because of 1 element in section +hint =Will not be shown because of 1 element in section +type =static + +value_1 =Joke-oneliner + + +[param_group_7] +; empty group adds space between neighbours + + +[param_group_8] +title =Edit group +type =edit +prefix =" -i " + +value_1 =ed41 +hint_1 =This hint explains sense of first Edit field + +key_2 =This is unused value. Just demo, the way how to create empty Edit element + +value_3 =ed43 +;These Edits will not be shown 'coz "value_n" numeration broken: "value_4" lacked +value_5 =ed45 +value_6 =ed46 + + +[param_group_9] +title =Choose your chair: +hint =Kukarek +type =radio + +; ampersand points at hotkey symbol +value_1 =Shaped s&pades +key_1 =--spades +default_1 =1 +hint_1 =This option can be choosen by hotkey Alt+P + +value_2 =Jerked c&ocks +key_2 =--cocks +; two default radiobuttons. Of course, it's not normal. But will work as expected +default_2 =1 +hint_2 =This option can be choosen by hotkey Alt+O + + +[param_group_10] +title =Destination: +hint =Kuda-h +type =combobox + +value_1 =Default City +key_1 =--msk +; among two (or more) defaults will be chosen teh first one +default_1 =1 + +value_2 =Default City 2 +key_2 =--spb +; DS so DS +default_2 =1 + +; New Vasiouki — element that means nothing +value_3 =New Vasiouki + +; Muhosran' has no name +key_4 =--mhs + + +[param_group_12] +; This group never reach your screen 'coz [param_group_11] had not been created +type =checkbox +key_1 =--very-important-flag +default_1 =true + + + + +; An example of localization. You may force it with renaming this section to [lang] +[lang_rus] +tooltip_edit_exePath=Путь к исполнимому файлу +tooltip_edit_cmdLine=Сгенерированная командная строка +btn_start=&Запустить +tooltip_btn_start=Жать сюда +error_no_file=Файл "%s" не найден +error_no_path=Указанный путь не найден +error=Ошибка +error_unknown=Неопознанная ошибка 0x%08X \ No newline at end of file diff --git a/UnGUI/embedded_config.rc b/UnGUI/embedded_config.rc new file mode 100644 index 0000000..fd35023 --- /dev/null +++ b/UnGUI/embedded_config.rc @@ -0,0 +1 @@ +DEFAULT CONFIG "config_to_embed.ini" diff --git a/UnGUI/hui.exe b/UnGUI/hui.exe new file mode 100644 index 0000000..fc9fa24 Binary files /dev/null and b/UnGUI/hui.exe differ diff --git a/UnGUI/ini/LICENSE b/UnGUI/ini/LICENSE new file mode 100644 index 0000000..5818e8d --- /dev/null +++ b/UnGUI/ini/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2016 rxi + + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/UnGUI/ini/README.md b/UnGUI/ini/README.md new file mode 100644 index 0000000..e0f330d --- /dev/null +++ b/UnGUI/ini/README.md @@ -0,0 +1,63 @@ + +# ini +A *tiny* ANSI C library for loading .ini config files + +## Usage +The files **[ini.c](src/ini.c?raw=1)** and **[ini.h](src/ini.h?raw=1)** should +be dropped into an existing project. + +The library has support for sections, comment lines and quoted string values +(with escapes). Unquoted values and keys are trimmed of whitespace when loaded. + +```ini +; last modified 1 April 2001 by John Doe +[owner] +name = John Doe +organization = Acme Widgets Inc. + +[database] +; use IP address in case network name resolution is not working +server = 192.0.2.62 +port = 143 +file = "payroll.dat" +``` + +An ini file can be loaded into memory by using the `ini_load()` function. +`NULL` is returned if the file cannot be loaded. +```c +ini_t *config = ini_load("config.ini"); +``` + +The library provides two functions for retrieving values: the first is +`ini_get()`. Given a section and a key the corresponding value is returned if +it exists. If the `section` argument is `NULL` then all sections are searched. +```c +const char *name = ini_get(config, "owner", "name"); +if (name) { + printf("name: %s\n", name); +} +``` + +The second, `ini_sget()`, takes the same arguments as `ini_get()` with the +addition of a scanf format string and a pointer for where to store the value. +```c +const char *server = "default"; +int port = 80; + +ini_sget(config, "database", "server", NULL, &server); +ini_sget(config, "database", "port", "%d", &port); + +printf("server: %s:%d\n", server, port); +``` + +The `ini_free()` function is used to free the memory used by the `ini_t*` +object when we are done with it. Calling this function invalidates all string +pointers returned by the library. +```c +ini_free(config); +``` + + +## License +This library is free software; you can redistribute it and/or modify it under +the terms of the MIT license. See [LICENSE](LICENSE) for details. diff --git a/UnGUI/ini/src/ini.c b/UnGUI/ini/src/ini.c new file mode 100644 index 0000000..56c0566 --- /dev/null +++ b/UnGUI/ini/src/ini.c @@ -0,0 +1,295 @@ +/** + * Copyright (c) 2016 rxi + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include + +#include +#include +#include +#include + +#include "ini.h" + +struct ini_t { + char *data; + char *end; +}; + +// Replaced with strcmpi from msvcrt +/* Case insensitive string compare */ +/* +static int strcmpci(const char *a, const char *b) { + for (;;) { + int d = tolower(*a) - tolower(*b); + if (d != 0 || !*a) { + return d; + } + a++, b++; + } +} +*/ + +/* Returns the next string in the split data */ +static char* next(ini_t *ini, char *p) { + p += strlen(p); + while (p < ini->end && *p == '\0') { + p++; + } + return p; +} + +static void trim_back(ini_t *ini, char *p) { + while (p >= ini->data && (*p == ' ' || *p == '\t' || *p == '\r')) { + *p-- = '\0'; + } +} + +static char* discard_line(ini_t *ini, char *p) { + while (p < ini->end && *p != '\n') { + *p++ = '\0'; + } + return p; +} + + +static char *unescape_quoted_value(ini_t *ini, char *p) { + /* Use `q` as write-head and `p` as read-head, `p` is always ahead of `q` + * as escape sequences are always larger than their resultant data */ + char *q = p; + p++; + while (p < ini->end && *p != '"' && *p != '\r' && *p != '\n') { + if (*p == '\\') { + /* Handle escaped char */ + p++; + switch (*p) { + default : *q = *p; break; + case 'r' : *q = '\r'; break; + case 'n' : *q = '\n'; break; + case 't' : *q = '\t'; break; + case '\r' : + case '\n' : + case '\0' : goto end; + } + + } else { + /* Handle normal char */ + *q = *p; + } + q++, p++; + } +end: + return q; +} + + +/* Splits data in place into strings containing section-headers, keys and + * values using one or more '\0' as a delimiter. Unescapes quoted values */ +static void split_data(ini_t *ini) { + char *value_start, *line_start; + char *p = ini->data; + + while (p < ini->end) { + switch (*p) { + case '\r': + case '\n': + case '\t': + case ' ': + *p = '\0'; + /* Fall through */ + + case '\0': + p++; + break; + + case '[': + p += strcspn(p, "]\n"); + *p = '\0'; + break; + + case ';': + p = discard_line(ini, p); + break; + + default: + line_start = p; + p += strcspn(p, "=\n"); + + /* Is line missing a '='? */ + if (*p != '=') { + p = discard_line(ini, line_start); + break; + } + trim_back(ini, p - 1); + + /* Replace '=' and whitespace after it with '\0' */ + do { + *p++ = '\0'; + } while (*p == ' ' || *p == '\r' || *p == '\t'); + + /* Is a value after '=' missing? */ + if (*p == '\n' || *p == '\0') { + p = discard_line(ini, line_start); + break; + } + + if (*p == '"') { + /* Handle quoted string value */ + value_start = p; + p = unescape_quoted_value(ini, p); + + /* Was the string empty? */ + if (p == value_start) { + p = discard_line(ini, line_start); + break; + } + + /* Discard the rest of the line after the string value */ + p = discard_line(ini, p); + + } else { + /* Handle normal value */ + p += strcspn(p, "\n"); + trim_back(ini, p - 1); + } + break; + } + } +} + + + +//ini_t* ini_load(const char *filename) { +// ini_t *ini = NULL; +// FILE *fp = NULL; +// int n, sz; +// +// /* Init ini struct */ +// ini = malloc(sizeof(*ini)); +// if (!ini) { +// goto fail; +// } +// memset(ini, 0, sizeof(*ini)); +// +// /* Open file */ +// fp = fopen(filename, "rb"); +// if (!fp) { +// goto fail; +// } +// +// /* Get file size */ +// fseek(fp, 0, SEEK_END); +// sz = ftell(fp); +// rewind(fp); +// +// /* Load file content into memory, null terminate, init end var */ +// ini->data = malloc(sz + 1); +// ini->data[sz] = '\0'; +// ini->end = ini->data + sz; +// n = fread(ini->data, 1, sz, fp); +// if (n != sz) { +// goto fail; +// } +// +// /* Prepare data */ +// split_data(ini); +// +// /* Clean up and return */ +// fclose(fp); +// return ini; +// +//fail: +// if (fp) fclose(fp); +// if (ini) ini_free(ini); +// return NULL; +//} + +ini_t* ini_load(HLOCAL fp, DWORD sz) { + ini_t *ini = LocalAlloc(LMEM_FIXED|LMEM_ZEROINIT, sizeof(ini_t)); + + ini->data = fp; + ini->end = (char*)ini->data + sz; + + split_data(ini); + + return ini; +} + +//void ini_free(ini_t *ini) { +// free(ini->data); +// free(ini); +//} + +void ini_free(ini_t *ini) { + LocalFree(ini->data); + LocalFree(ini); +} + + +const char* ini_get(ini_t *ini, const char *section, const char *key) { + char *current_section = ""; + char *val; + char *p = ini->data; + + if (*p == '\0') { + p = next(ini, p); + } + + while (p < ini->end) { + if (*p == '[') { + /* Handle section */ + current_section = p + 1; + + } else { + /* Handle key */ + val = next(ini, p); +// if (!section || !strcmpci(section, current_section)) { + if (!section || !strcmpi(section, current_section)) { +// if (!strcmpci(p, key)) { + if (!strcmpi(p, key)) { + return val; + } + } + p = val; + } + + p = next(ini, p); + } + + return NULL; +} + + +int ini_sget( + ini_t *ini, const char *section, const char *key, + const char *scanfmt, void *dst +) { + const char *val = ini_get(ini, section, key); + if (!val) { + return 0; + } + if (scanfmt) { + sscanf(val, scanfmt, dst); + } else { + *((const char**) dst) = val; + } + return 1; +} diff --git a/UnGUI/ini/src/ini.h b/UnGUI/ini/src/ini.h new file mode 100644 index 0000000..bde9fe8 --- /dev/null +++ b/UnGUI/ini/src/ini.h @@ -0,0 +1,22 @@ +/** + * Copyright (c) 2016 rxi + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the MIT license. See `ini.c` for details. + */ + +#ifndef INI_H +#define INI_H + +#define INI_VERSION "0.1.1" + +typedef struct ini_t ini_t; + +//ini_t* ini_load(const char *filename); +ini_t* ini_load(HLOCAL fp, DWORD sz); + +void ini_free(ini_t *ini); +const char* ini_get(ini_t *ini, const char *section, const char *key); +int ini_sget(ini_t *ini, const char *section, const char *key, const char *scanfmt, void *dst); + +#endif diff --git a/UnGUI/upx.mak b/UnGUI/upx.mak new file mode 100644 index 0000000..096ee68 --- /dev/null +++ b/UnGUI/upx.mak @@ -0,0 +1,3 @@ +all-after: + @chcp 1251 > nul + @upx $(BIN) > nul \ No newline at end of file