Получение синхронных данных с помощью микросхемы Cypress CY7C68013A (FX2LP) на примере устройства видеозахвата для старых компьюторов.

Постановка задачи.

     Так произошло, что у меня накопилось несколько советских компьютеров, и однажды у меня возникло желание их включить, однако свободного телевизора под рукой не оказалось. Решить данную проблему можно несколькими способами: приобрести телевизор или tv-тюнер, собрать удвоитель числа строк и подключить его к монитору с VGA разъемом или послать видео поток на компьютер и там его обработать. В данной статье будет рассмотрен последний вариант.
скриншот БК0010-01

Железо.

     Из компьютеров нам нужно вывести логические уровни, соответствующие цветовым составляющим R G B, уровень яркости и совмещенный сигнал синхронизации. Также нужно вывести тактовый сигнал, период которого равен периоду следования пикселей, т.н. pixel clock. Во всех моих компьютерах используется по одному биту на цветовую компоненту и один бит яркости. Частота следования пикселей 7, 12, и 12.5 МГц для zx-spectrum, БК0010-01 и МС-0511 соответственно.
     Первый вариант устройства был сделан на ПЛИС+ОЗУ+FT232H. Из-за медленного ОЗУ передавался только каждый второй кадр, да и вообще устройство получилось слишком сложное для такой задачи. Плюсом такого подхода является то, что вместо FT232H можно использовать любую микросхему, испытывались варианты с stm32 и at90usb82, в обоих случаях частота обновления изображения составляла порядка 5 FPS.
     Текущая версия устройства практически состоит лишь из одной микросхемы CY7C68013A серии FX2. Я использовал плату LCSOFT CY7C68013A mini board, которая стоит меньше $10 в интернет-магазинах. Данный чип представляет собой гибкий в настройке USB-контроллер, совмещенный с ядром микроконтроллера 8051, причем микроконтроллер обычно не участвует (но может) в обмене данных, а служит только для инициализации этого процесса. В данном устройстве микросхема используется в режиме GPIF (general pragrammable interface). В этом режиме для управления потоком данных используется аппаратный конечный автомат, который позволяет реализовать многие протоколы на высокой скорости и без участия микроконтроллерного ядра. Именно на этой микросхеме и именно в таком режиме работают многие дешевые USB логические анализаторы Saleae logic, USBee и их клоны. По сути, работа такого устройства отличается от работы логического анализатора только тем, что данные тактируются внешним тактовым сигналом.
     Сигналы R,G,B, яркости и синхронизации подаются на вход GPIF (PB0-PB7), а тактовый сигнал на вход ifclk. Сигналы передаются на компьютер без какой-либо обработки, поэтому они могут быть инвертированы или их количество может доходить до 8 или даже 16.

Программное обеспечение.

     На используемой плате присутствует EEPROM, но он отключен, что устанавливает VID/PID микросхемы в значения по умолчанию. Особенностью данной микросхемы является то, что она не содержит долговременной памяти и может получать прошивку через USB.
     Поскольку принцип действия устройства мало отличается от логического анализатора, в качестве основы для прошивки был использован открытый исходный код из проекта sigrok для анализатора на этой микросхеме:fx2lafw.
Были внесены следующие изменения:
  • Вывод ifclk настроен на вход, и используется для тактирования GPIF.
    
    void gpif_init_la(void)
    {
    	/*
    	 * Setup the FX2 in GPIF master mode, using the external clock
    	 * (not inverted), and using async sampling.
    	 */
    	IFCONFIG = 0x4e; //0xee
            ...
    
  • Аппаратный конечный автомат был перенастроен таким образом, что данные передавались каждый такт, т.е. удалены состояния задержки из цикла. Также был установлен бит re-execute.
    
    static void gpid_make_data_dp_state(volatile BYTE *pSTATE)
    {
    	/*
    	 * BRANCH
    	 * Branch to IDLE if condition is true, back to S2 otherwise.
    	 * re-execute
    	 */
    	pSTATE[0] = (7 << 3) | (2 << 0) | (1 << 7);
    
    	/*
    	 * OPCODE
    	 * SGL=0, GIN=0, INCAD=0, NEXT=0, DATA=1, DP=1
    	 */
    	pSTATE[8] = (1 << 1) | (1 << 0);
    
    	/*
    	 * OUTPUT
    	 * OE[0:3]=0, CTL[0:3]=0
    	 */
    	pSTATE[16] = 0x00;
    
    	/*
    	 * LOGIC FUNCTION
    	 * Evaluate if the FIFO full flag is set.
    	 * LFUNC=0 (AND), TERMA=6 (FIFO Flag), TERMB=6 (FIFO Flag)
    	 */
    	pSTATE[24] = (6 << 3) | (6 << 0);
    }
    

  • Добавлена возможность инверсии тактового сигнала. В команду запуска добавлен соответствующий флаг.
    
    bool gpif_acquisition_start(const struct cmd_start_acquisition *cmd)
    {
    //	int i;
    	volatile BYTE *pSTATE = &GPIF_WAVE_DATA;
    
    	/* Ensure GPIF is idle before reconfiguration. */
    	while (!(GPIFTRIG & 0x80));
    
    	/* Configure the EP2 FIFO. */
    	if (cmd->flags & CMD_START_FLAGS_SAMPLE_16BIT) {
    		EP2FIFOCFG = bmAUTOIN | bmWORDWIDE;
    	} else {
    		EP2FIFOCFG = bmAUTOIN;
    	}
    	SYNCDELAY();
    
    	/* Set IFCONFIG to the correct clock source. */
    	if (cmd->flags & CMD_START_FLAGS_INV_CLK) {
    		IFCONFIG = 0x5e;
    	}
    	/* Populate delay states. */
    		gpif_make_delay_state(pSTATE++, 0);  // 256 tiks delay
    		gpif_make_delay_state(pSTATE++, 0);  // 256 tiks delay
    		
    
    	/* Populate S2 - the decision point. */
    	gpid_make_data_dp_state(pSTATE++);
            ...
    

Программа для fx2 транслируется компилятором sdcc традиционным для юникс-подобных систем способом — командами configure && make. В результате получается файл в формате intel hex.
     Так как все писалось под linux, нет необходимости писать драйвер, достаточно воспользоваться библиотекой libusb. Hex-файл прошивки преобразуется в C-файл в виде массива строк, который потом загружается в CY7C68013A при запуске программы. Для начала процесса получения данных необходимо передать vendor request с параметрами и флагами, затем читать данные из конечной точки (endpoint), привязанной к GPIF (в данном случае EP2). Для того, чтобы был запас по времени, в конечный автомат микросхемы были добавлены два состояния с задержкой по 256 тактов, в которые он последовательно переходит после старта.
     После того, как я послал команду на старт и прочитал данные из конечной точки 2 функциями синхронного В/В libusb, я получил следующее:

void usb_send_start_cmd(void)
{
	int rv;
	struct cmd_start_acquisition cmd=
	{
		.flags=CMD_START_FLAGS_SAMPLE_8BIT | CMD_START_FLAGS_INV_CLK,
		.sample_delay_h=0,
		.sample_delay_l=0
	};
	rv=libusb_control_transfer(device_h,0x40,CMD_START,
			/*addr=*/0,0,
			/*buf=*/(char*)&cmd,/*size=*/3,
			/*timeout=*/1000/*msec*/);
if(rv<0)
	fprintf(stderr,"Vendor request error\n");
else
	printf("Starting sample data\n");
	
}

#define N 65536
void usb_test(void)
{
	int rv,i;
	struct version_info ver;
	uint8_t *buf;
	int transfered=0;
	FILE *f;
	
	rv=libusb_control_transfer(device_h,0xc0,CMD_GET_FW_VERSION,
			/*addr=*/0,0,
			/*buf=*/(char*)&ver,/*size=*/2,
			/*timeout=*/1000/*msec*/);
if(rv<0)
	{
	    fprintf(stderr,"Vendor request error\n");
	    return;
	}
	printf("Version %d.%d\n",ver.major,ver.minor);
	buf=malloc(N);
	usb_send_start_cmd();

	rv=libusb_bulk_transfer(device_h,ep2in,(char *)buf,N,&transfered,1000);
	if(rv<0)
	{
	    fprintf(stderr,"Bulk transfer error\n");
	    return;
	}
	printf("transfered %lu bytes\n", transfered);
	f=fopen("/tmp/usb_log.dat","w");
	for(i=0;i<transfered;i++)
	{
	    fprintf(f,"%d\n",buf[i]);
	}
	fclose(f);

}


Полученные данные
Казалось бы, данные получены, достаточно распарсить и отрэндерить, но выяснилось, что между вызовами синхронных фукций libusb проходит достаточно много времени и FIFO CY7C68013A переполняется, что приводит к переходу конечного автомата в состояние idle. Решить проблему можно используя асинхронные функции libusb.

int usb_start_transfer (void) 
{
    uint8_t i;
    uint8_t *usb_buf;
    struct libusb_transfer *xfr;
    
    usb_send_start_cmd();
    for(i=0;i<N_OF_TRANSFERS;i++)
    {
	usb_buf=malloc(USB_BUF_SIZE);
	xfr = libusb_alloc_transfer(0);
	libusb_fill_bulk_transfer(xfr, device_h, ep2in, usb_buf, USB_BUF_SIZE, callbackUSBTransferComplete, NULL, 1000 );
    
	if(libusb_submit_transfer(xfr) < 0)
	{
	    // Error
	    libusb_free_transfer(xfr);
	    free(usb_buf);
	    fprintf(stderr,"USB submit transfer %d error\n",i);
	}
    }


void callbackUSBTransferComplete(struct libusb_transfer *xfr)
{
    switch(xfr->status)
    {
        case LIBUSB_TRANSFER_COMPLETED:
            // Success here, data transfered are inside 
            // xfr->buffer
            // and the length is
            // xfr->actual_length
	    data_ready_cb((void *)xfr->buffer, xfr->actual_length);
	    if(libusb_submit_transfer(xfr) < 0)
	    {
		// Error
		libusb_free_transfer(xfr);
		free(xfr->buffer);
		fprintf(stderr,"USB resubmit transfer error\n");
		stop=1;
	    }
            break;
        case LIBUSB_TRANSFER_CANCELLED:
	    fprintf(stderr,"USB transfer error: canceled\n");
	    stop=1;
            break;
        case LIBUSB_TRANSFER_NO_DEVICE:
	    fprintf(stderr,"USB transfer error: no device\n");
	    stop=1;
            break;
        case LIBUSB_TRANSFER_TIMED_OUT:
	    fprintf(stderr,"USB transfer error: time out\n");
	    stop=1;
            break;
        case LIBUSB_TRANSFER_ERROR:
	    fprintf(stderr,"USB transfer error\n");
	    stop=1;
            break;
        case LIBUSB_TRANSFER_STALL:
	    fprintf(stderr,"USB transfer error: stall\n");
	    stop=1;
            break;
        case LIBUSB_TRANSFER_OVERFLOW:
            // Various type of errors here
	    fprintf(stderr,"USB transfer error: overflow\n");
	    stop=1;
            break;
    }
}


Важно заметить, что НУЖНО создавать несколько трансферов, они ставятся в очередь, и как только один из них завершиться, следующий сразу же подхватывает принятие данных, причем это происходит в ядре ОС с минимальными задержками. По завершении трансфера вызывается функция-callback, где и происходит обработка данных. Также важно понимать, что libusb не использует многопоточность, поэтому необходимо переодически вызывать функцию libusb_handle_events в основном цикле программы и передавать управление библиотеке.
     Полученные данные обрабатываются, выделяются строчные и кадровые импульсы и формируется буфер изображения. Для выделения кадровых импульсов используется простой метод интегрирования, который зарекомендовал себя еще на варианте с ПЛИС:

uint8_t v_detect(uint8_t c)
{
	static uint16_t s_counter;
	if(s_counter != 65535)
	{
		if(c & S_BIT) s_counter++;
	}
	if(s_counter!=0)
	{
		if(!(c & S_BIT)) s_counter--;
	}
	if(s_counter>74) return 1;
	else return 0;
}

При наличии единицы в бите синхросигнала происходит увеличение счетчика, при нуле — уменьшение. Если счетчик превысил определенный порог (подбирается экспериментально) — значит зафиксирован кадровый синхроимпульс.
Цвет пикселя формируется из значений битов R,G,B и яркости для каждого компютера отдельно, также есть возможность переключить в монохромный режим по клавише scroll lock.
     Отрисовка изображения происходит в отдельном потоке с помощью библиотек SDL и OpenGL, буфер экрана «натягивается» как текстура на прямоугольник, который сохраняет пропорции при масштабировании окна. При масштабировании текстура фильтруется, что дает теплый-ламповый ностальгический эффект.
Весь код размещен на github.
МС0511БК0010-01 кругиZX-spectrum круги

Комментарии (71)

RSS свернуть / развернуть
А как насчет поддержки винды? Насколько я вижу, все примененные решения — вполне кроссплатформенны.
0
  • avatar
  • Vga
  • 11 июля 2015, 17:09
Я пока не собирал под виндовс, как-нибудь попробую. Особых проблем быть не должно, у меня даже многопоточность используется через SDL.
0
А что оно у тебя выдает, если подцепить голую CY7C63013, без подключенного к ней компа? Я кое-как собрал, добрался до того, что оно выдает пять таймаутов и висит, не реагируя на Ctrl-C в консоль. Окно не появляется (правда, я не ковырял код инициализации SDL/OpenGL на предмет виндоус-совместимости, но, по крайней мере, glx-инита в коде нет, иначе бы оно отказалось собираться).
0
~$ vcaptfx2
Starting sample data
USB error event
USB transfer error: time out
USB transfer error: time out
Waiting video stream to close

Окно должно появиться и сразу закрыться. Инициализация окна происходит в отдельном thread'е и если она не удастся, то выведет «Can't set OpenGL mode». Похоже, что thread не стартует. Функция video_ounput в render.c
0
Окна нет, в консоль бесконечно спамит «Waiting video stream to close». «USB error event» тоже отсутствует.
Надо подсунуть ему имитатор потока данных, но для этого надо код глубже ковырять. Хотя, честно говоря, код вызывает желание переписать его нахрен.

Кстати, зачем ты использовал OpenGL? Насколько я знаю, с выводом картинки SDL и сам справится.
0
Код собирался из разных кусков предыдущих версий, особенно тридинг плохо прикручен. Я вообще не думал, что кто-нибудь будет использовать его напрямую. Можно попробовать переписать с более продуманной архитектурой и опциональным OpenGL, но будет ли это кому-нибудь нужно? OpenGL используется для масштабирования изображения с фильтрацией, SDL, на сколько я знаю, может только выводить 1:1 либо с программным масштабированием. Одна из предыдущих версий программы вообще на directfb была. В принципе, у меня получилось собрать в code::blocks в mingw32, но при запуске он даже в консоль ничего не пишет.
0
Программное масштабирование — это, конечно, медленно. Хотя в данном случае можно ограничиться кратным масштабированием — что довольно быстро. Или напротив, прикрутить такие фильтры, как 2xSaI. Хотя смысла отказываться от OpenGL тоже нет.

Там, я бы сказал, все плохо даже не доходя до архитектуры :) Глобальные переменные (к одной из которых, кажется, линкуется вызов функции done() из main.c), хотя бы.
+1
По мне, так глобальные переменные — удобный способ взаимодействия с тредами. А есть вообще другие способы?
0
Во-первых, глобальные переменные зло)
Во-вторых, с тредами, по идее, взаимодействовать следует средствами синхронизации. Возможно, дополнительно к этому — глобальные переменные, доступ к которым защищается синхронизацией.
В третьих, у тебя их слишком много. Глобальны, ЕМНИП, даже переменные, которые используются всего одной функцией, не говоря уже о том, чтобы ограничить ключевым словом static видимость глобальных переменных файла.
Ну и как я уже сказал, вызов функции done в main.c линкуется на переменную done в keys.c. По идее, это вообще к падению на выходе должно приводить. С вообще поражает меня своей способностью скомпилировать некомпилируемое. Delphi такого не позволяет.
+1
Меня это не оправдывает, но keys.c вообще был написан в 2007 году :) У меня done не слинковываются, даже варнинг не выдает, возможно он выкидывает пустую функцию при компиляции, а файлы компилируются по-отдельности в .o, и линкер ее не видит.
0
Вот как раз линкует он молча. А функция done_() в main.c так и остается висеть мертвым кодом.
+1
И вообще, я вон внизу сборку выложил, потести плиз.
0
Большое спасибо за статью! Она интересна содержательной частью, да и скриншоты навеяли ностальгию (первый ПК с которым я познакомился был БК0010, а «Спекртум» стал моим первый по настоящему «персональным» компьютером).

Кстати, я только недавно узнал (не прошло и 30 лет), что означает загадочная надпись «CAGLRCCC.R» в левом верхнем углу БК. Как оказалось, это подсказка горячих клавиш:

Каждый из этих символов - первый символ оператора (или команды), для набора которого на
клавиатуре достаточно одновременно нажать клавишу "АР2" и соответствующую
цифровую клавишу.

│ 1 │ COLOR 		│
│ 2 │ AUTO 		│
│ 3 │ GOTO 		│
│ 4 │ LIST 		│
│ 5 │ RUN <BK> 		│
│ 6 │ COLOR 1,0 <BK> 	│
│ 7 │ CLOAD" 		│
│ 8 │ CONT <BK> 	│
│ 9 │ . <BK> 		│
│ 0 │ <СБР> RUN <BK> 	│

Честно говоря, я бы тогда (да и сейчас) никогда бы самостоятельно до этого не додумался…

З.Ы. Т.к. коллега well-man2000 обвинил меня в том, что я излишне критикую код других членов сообщества – я просто обязан сделать Вам замечание: на скриншоте Спекртума, строка номер 6,
6 LET b = RND * 1.9
Но дальше вы эту переменную (b) дальше в коде не используете!1111 :))
+3
Да, забыл удалить. Я сначала хотел еще менять атрибут яркости командой BRIGHT b, но оказалось, что он может быть изменен только для всего экрана. Уверен это можно обойти с помощью пары волшебных POKE с адресами системных переменных.
+2
Да, забыл удалить

Эм, коллега, касательно кода для Спекртум – извиняюсь, я просто неудачно пошутил.

Это была ирония, просто well-man2000 намекнул, что я слишком придирчиво отношусь к чужому коду, и я решил продемонстрировать утрированный пример… (придраться к коду, который на скриншоте… на BASIC… для Spectrum …)
0
Ну косяк есть косяк, тем более на интерпретируемом бейсике простой LET с RND оказывает существенный удар по производительности! :)
+1
Выложил текущую сборку. У меня не осталось ни единого работоспособного спекки, да и искать куда подключаться в MiniLogic'е лень.
0
  • avatar
  • Vga
  • 15 июля 2015, 21:49
Запускается, и даже изображение показывает иногда, но сразу падает с usb transfer error: time out.
0
Интересно. Как минимум это означает, что графическая часть вполне работоспособна.
А что, собственно, означает эта ошибка? Я собирал в tcc, он практически не оптимизирует, поэтому математика вроде перекодирования картинки в текстуру может тормозить. Или это проблема с получением данных от железяки?
Подправленные сырки с проектом для Dev-C++, можешь попробовать собрать их под винду mingw'ом, если дело в нехватке производительности кода — может помочь.
0
P.S. Фиксы грязные, так что под никсы оно теперь не соберется. Кроссплатформенную версию сырков надо еще собирать из этих двух)
0
Очень похоже на то, что не успевают обрабатываться данные. Хотя там обработка очень простая, только целочисленная.
0
Сейчас обнаружил, что в Makefile забыл добавить опцию компилятора -Wall. Ужаснулся от кол-ва варнингов.
+1
Код местами ужасает больше, чем варнинги (большая часть из которых из-за странного подхода к хедерам). Моя версия собирается с 3 варнингами, жалующимися на отсутствие объявления для функции sleep (но, тем не менее, линкер ее откуда-то подтягиевает; ох уж этот си!).
+1
Вообще-то не должен, mingw не подставляет, но в данном случае этот sleep вообще не нужен.
0
Я бы направил его на виндовый Sleep(), собственно, скорее всего, он и подтягивается, т.к. задержки оно принимает в ms (в отличие от POSIX'овых sleep/usleep, принимающих s и us).
+1
Пересобрал — тоже самое, причем с графикой тоже не все в порядке, окно зависает. Думаю добавить тестовый массив данных для отладки графики, после чистки кода :)
0
IMHO, проблема где-то рядом с sleep(), usleep()

Например, у вас в коде:


            while(video_stop!=2)
            {
				usleep(1000000);
				printf("Waiting video stream to close\n");
			}


Вот реализация usleep от mingw:


#define WIN32_LEAN_AND_MEAN
#include <windows.h>

int __cdecl usleep(useconds_t useconds)
{
    if(useconds == 0)
        return 0;

    if(useconds >= 1000000)
        return EINVAL;

    Sleep((useconds + 999) / 1000);

    return 0;
}


Там значене 1000000 выходит за границу допустимых значений, и вместо задержки мы получим моментальный выход из функции с ошибкой EINVAL.
0
Ну конкретно здесь это не критично, здесь просто ожидается завершения треда и эта задержка влияет на частоту «спама» в консоль.
0
Ну, я же сказал, что «истина где-то рядом»,
IMHO, проблема где-то рядом с sleep(), usleep()

Просто я заметил явное ограничение для 1000000 в данной реализации usleep().

Более интересно то, что накопал Vga – для sleep() нет предекларации, но все-же с чем-то она линкуется…
0
С С это вечная проблема. То оно подтянулось из какого-то хедера (искать его без нормальной IDE — отдельное веселье), но не линкуется, то вот так)
0
Ну это только в винде так, в linux все лежит на своих местах, да и утилиты типа pkg-config избавляют от ручного прописывания путей.
0
Не, это везде так, особенность процесса компиляции в С. Чаще всего обусловлено тем, что в линковку не включена библиотека, где требуемый символ (с TCC еще весьма часто встречается ситуация, когда в хедерах функция есть, а в библиотеке нет, преимущественно в плане POSIX-функций — связано это с тем, что хедеры у него мингвовские, а вот библиотека своя, подрезанная).
Что до «то вот так» — функция sleep оказалась в какой-то из включенных библиотек (вероятно, kernel32), а декларацию С для нее автоматически сгенерил и она совпала с реальной.
0
Это зависит от реализации, в моей сборке все равно используется спячка в миллисекундах. Там еще есть в модуле render слип, на 1мс. Может и он гадит.
А вообще, переписать все нафиг...)
0
Закомитил «подчищенную» версию с более правильной обработкой USB трансферов, и без варнингов.
0
Теперь оно и под винду без проблем собирается. Только
void callbackUSBTransferComplete(struct libusb_transfer *xfr);

надо исправить на
0
*исправить на
LIBUSB_CALL void callbackUSBTransferComplete(struct libusb_transfer *xfr);
0
Спасибо, добавил.
Текущая версия собирается и работает на mingw32. Сейчас избавился от почти всех глобальных переменных и добавил поддержку чтения/записи настроек из конфиг-файла, позволяющая настраивать параметры компьютеров без перекомпиляции.
0
Выложил новую версию с поддержкой конфигов. Там же (на гитхабе) есть и собранный для винды бинарник, однако у меня возникли проблемы с его тестированием, если точнее с бэкендом libusb. У меня он запускается и работает, иногда. Проблема точно в USB.
Естественно, в линуксе все работает без нареканий. :)
0
Какой драйвер на устройство ставил? Какого рода проблемы вылазят?
0
С помощью zadig.exe ставил разные, с libusb-win32 у меня иногда работает, но на сколько я знаю это драйвер для libusb0.1. WinUSB не ставится, может проблема в том, что винда x86_64?
0
Я ставил WinUSB, Win7x64, ставятся. С libusb-win32 не работает.
0
git плохо дружит с блобами, лучше бинарники положить в «релизы» на гитхабе.
0
Мне он ничего не сказал, да и через web-интерфейс доступны.
0
И не скажет. Но это не меняет того, что гит с такими файлами работает неэффективно. Лучше (да и удобнее для юзера) положить бинарную сборку в релизы.
0
Объемы файлов такие, что на эффективности это не скажется.
(На самом деле, я просто не нашел как это сделать) :)
0
Вроде все довольно просто — открываешь на гитхабе свой репозиторий, переходишь на вкладку Releases и там выбираешь Create a new release.
0
Да, не сразу я эту кнопку нашел :)
0
Да, примерно так, но бинарники лучше собрать в zip.
0
Zip — не нужен и должен умереть! Следующий релиз соберу в 7zip.
0
Полностью согласен) Но я полагаю, ты и этот отредактировать можешь.
0
Выложил новую версию, работает под виндовс с бэкендом libusb0 (хоть и не должна). Осталось только победить перенаправление в/в в SDL.
0
Вылечилось пересборкой SDL с нужным флагом.
0
Сделай еще чтобы оно на винде рядом с собой конфигом сорило, а не в ~.
0
Тогда нужно запускать с параметрами -m -f <имя файла>, -m обязательно.
0
Довольно неудобно каждый раз так запускать. Для многих программ достаточно создать конфиг в каком-то из поддерживаемых ей мест и она автоматически будет работать с ним, это, на мой взгляд, довольно удобно и оставляет выбор юзеру.
0
-m обязательно.
нафига? Традиционное решение ini-файл рядом с экзешником с наперед заданным именем. А вот -m уже опционально если хочется запустить с другим, не дефолтным конфигом.
0
Размещение конфига рядом с екзешником действительно традиционное решение… для windows 95 — 98. Обязательность -m будет пофикшена скоро.
0
Это решение и сейчас вполне востребовано. Вы ведь пишете не прогу по всем гайдлайнам мелкомягких в том числе и с инсталлером, а так, мелкую тулзу. Так почему ей не быть портабельной? И инишник рядом с экзешником вполне годное решение. Хоть и давно объявленное deprecated…
+1
для windows 95 — 98
И что? Плохо, при переустановке системы не будут слетать все настроки? Плохо, что можно перебросить программку на другой комп или дать комунить настроенную?
0
Я так понимаю, это сделано для того, чтобы у каждого пользователя были свои настройки. Еще бы майкрософт «придумал» хранить папку users на отдельном разделе, тогда можно было бы переустанавливать виндовс без потери данных. А у пользователя вообще не должно быть прав на запись в program files.
Это решается одним батником. Нужно просто запустить с опцией -c .
0
Еще бы майкрософт «придумал» хранить папку users на отдельном разделе, тогда можно было бы переустанавливать виндовс без потери данных.
Я выносил домашний директорий пользователя(-ей) на другой раздел, а оригинальную точку туда подлинковывал софт линком. Но это не сохранит настройки пользователя, поскольку пользовательские файлы реестра всё-таки будут переписываться при переустановке системы.
Вообще, виндовс только совсем недавно стала многопользовательской системой. Быть может, лет через -надцать они продвинутся в желаемом вами направлении. Если не рассыплются раньше.
0
А нужно ли, чтобы у пользователя были свои настройки, если это не какой-нибудь гламурный AIMP с шкурками, а утилитка? Тем более, ценой большого неудобства управления этими самыми настройками.

По крайней мере, стоит проверить не лежит ли progname.ini рядом с исполняемым файликом, если нету — тогда уже срать в аппдату.
0


Да-а-а, помню… Ка-а-к я юзал, ка-а-к я юзал ini-файлы в 90-х — более простого и лучшего средства для изменения значений глобальных переменных программы (просто самим юзером прямо в Notepad) без перекомпиляции — просто не было. Глупо поверив в чудодейственность сего, даже делал длиннющие ini-портянки, в которых были прописаны даже почти все параметры форм программы.

Потом долго надрачивал/юзал усиленно рекламируемый xml, но как-то все это было весьма-весьма геморойно — xml-технологии так и не стрельнули в итоге.

А в 2001-м вплотную познакомился с json в javascript (фанатом сего языка являюсь с 2000-го) — изумлению просто не было предела: не только какие-то переменные, а даже объекты и целые объектные модели со всеми потрохами можно было сохранять/восстанавливать в/из файла или передавать в другую программу на javascript на совершенно другом сайте (сериализация/десериализация объектов). Но тогда (начало 2000-х) эти технологии ограничивались только страничками в web'е.

Попробую вызвать батхерт у Лайфловера: How about json & javascript app, bro?
-1
Жабий скрипт не нужен.
0
Жабий скрипт не нужен.
Да нет, это воистину православный язык. А к жабе и к прочим .net он (ECMAScript) не имеет никакого отношения. Это был просто дешевый маркетинговый ход менеджеров (поминание жабы в названии).
0
Он хуже. Вообще, как давно и правильно было сказано, если хочешь что-то написать, пиши на Си. Не получается на Си — напиши на ассемблере.
0
Т.е. ты не любишь интерпретируемые языки, но ты же вроде был начинающим фанатом Перла? Так получилось исторически, что на x-nix на Си пишут все подряд, но не так на винде.
0
Перл — забавная штучка. На Си написан, в нём одно время даже оопа не было (появился под давлением моды >__<).
Главное, что не собирается писать на перле «app». Говнокодеры с дотнетами и оопами — потому что перл не любят, нормальные люди — потому что есть Си.
0
нормальные люди — потому что есть Си

Рады слышать, держи кардан, чувак, ты наш человек!
0
Если надо, юзер может создать ярлык с нужными параметрами, а %USERPROFILE%\AppData\Local\ есть рекомендованное в MSDN место для хранения конфигов. Собираюсь сделать более человеческий разбор параметров командной строки.
0
Есть еще такое понятие, как portable софт. Именно по этой причине многое ПО поддерживает несколько мест размещения конфигов и автоматически определяет, каким пользоваться.
0
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.