Вопросы по созданию приборов на С++ — Панели и приборы

…

В СДК есть пример:

 

 

PELEMENT_HEADER cs_sliders_list[] = {&cs_slider_trim.header, &cs_slider_ailerons.header, &cs_slider_elevator.header, &cs_slider_rudder.header, NULL};

 

Если я правильно понял, это обозначение последовательности отрисовки деталей или нет?

А вот на такие вопросы уже и не грех ответить.

 

Да, это список указателей на визуальные элементы. Используется симом в процессе обхода дерева приборов и их визуальных элементов. В каждом визуальном элементе есть указатель на next список визуальных элементов. Собственно из таких списков (в виде инициализированного статического массива, оканчивающегося элементом NULL) и строится дерево приборов и их визуальных элементов в dll (в gau). Ну и соответственно это дерево строится gau-писателем ведь не просто так. Именно через него сим осуществляет доступ к каждому визуальному элементу при прорисовке и именно имея указатели на приборы и их визуальные элементы сим 18 раз в секунду вызывает CallBack-функции всех приборов и визуальных элементов, если конечно они определены.

 

 

Важный момент.

Правильное построение дерева приборов и их визуальных элементов имеет очень важное значение (вернее будет — правильное построение поддеревьев (сучьев, веток, листьев), визуальных элементов в каждом приборе). Но это уже другая тема.

 

Почему именно дерево?

Потому, что это действительно дерево.

 

Корень вот:

// This is the module's export table.
GAUGESLINKAGE Linkage =
{
   0x00000013,
   module_init,
   module_deinit,
   0,
   0,
   0x800,
   {
	 GAUGE_TABLE_ENTRY(&gaugehdr_isd1)
	 GAUGE_TABLE_ENTRY(&gaugehdr_isd2)
	 GAUGE_TABLE_ENTRY(&gaugehdr_kurs_mp_selekt)
....

 

Корень — структура Linkage. Далее список сучьев — приборов (после после 0x800). Ну а далее в каждом приборе, ветвление визуальных элементов.


Edited by serg_p

Спасибо Сергей за ответы, сорри, если не совсем точно могу задать свой вопрос.

 

PELEMENT_HEADER tank_2_list_2[] = {&tank_2_03.header, NULL};

Это определение статической переменной, которая тут же и инициализируется. Т.к. tank_2_list_2[] — это массив, то инициализация здесь производится не одним значением, а списком значений. NULL — это тоже значение. По этому значению симулятор определяет, что это конец массива. Стандартная практика в C.

 

{(PELEMENT_HEADER)&tank_2_03, NULL};

и

{&tank_2_03.header, NULL};

Это одно и тоже, за исключением того, что в верхней строке — явное приведение типа указателя, это хорошая практика, приведение типов осуществлять самому, т.е. явно. & — операция взятия адреса.

 

Здесь я просто засомневался в синтаксисе, для чего эти строки я прекрасно понимаю.

 

Вот например

C++. Бархатный путь

 

На эту книгу я наткнулся, введя сейчас в гугле: «книга C++ элементарные понятия».

 

 

Спасибо за ссылку, большой объем информации, самый полный для меня на сегодня.

Для меня интернет — огромная помойка, думаю не только для меня, и когда ищешь то, что тебе нужно, обязательно вылезет куча хлама, а в нём найти что-то нужное весьма затруднительно.

Вот то, что я читал и практиковал, может кому сгодится:

http://shatalov.su/r…/cpp_primer.php

http://kvodo.ru/category/lessons

http://gendocs.ru/v3…33/лекции_по_cс

http://iguania.ru/

http://www.cppstudio…-vybora-if-else

 

Для подключения звука:

http://www.gamedev.r…/ds_usage_basic

http://exelab.ru/pro…=zart126<br />http://subscribe.ru/…54450.html#ds41

 

Естественно нигде нет полной информации для самостоятельного нормального обучения. Не думайте, что я «халявщик» и ничего не делаю для изучения. Для правильной обработки полученной информации нужен опыт в работе С++, просто примеры — этого мало. Нужен контроль, хоть какой-то. А то понял не совсем так и потом понеслось… Да и память у человека не компьютерная. Для переваривания каши в голове — практика, практика и еще раз практика. Появляются знания, появляются вопросы… Почитаешь форумы по С++, иногда волосы дыбом, хоть бы раз попал на форумы, где могут грамотно дать ответ на конкретный вопрос, а то и вовсе отправляют книги читать. Вот и приходится спрашивать тонкости для меня, для вас профи — элементарщина.


Edited by seyco

Ну да, соглашусь, в русском инете — сплошные какие-то ублюдки. Неужели человечество вымерает?

(Хотя, кое-где встречаются люди, из-за которых еще теплится свет.)

 

Хорошая книга — вот верный друг. И лучше в твердой копии.

Конечно вопрос, где найти.

Но, как ни странно, этот сраный тырнет может в этом помочь.

Правда тут есть одна трудность. Если ты еще слаб в теме, то трудно определиться с тем, что именно тебе подходит. Тут конечно очень могут помочь советы тех кто знает. Но можно и самому наткнуться на стоящее. Так бывает. У меня не раз было. Я очень желал хорошую книгу по интересующей теме и она находилась.

 

До определенного времени для меня Windows API был темный лес.

Пока мне не посоветовали почитать:

Джеффри РИХТЕР, Создание эффективных WIN32-приложений с учётом специфики 64-разрядной версии Windows

(Два издания этой книги я скачал из интернета)

Я бы этому человеку крепко пожал бы руку, ели бы довелось встретится.

 

По поводу програмирования для сима, Андрей icebear в свое время говорит про книгу Грифитcа (не знаю, правильно ли я пишу имя). То же, как я понял из его слов очень полезная книга для программистов работающих с симом.

 

Еще есть сайт:

http://www.fsdeveloper.com/

Кажется тут видел не раз самого Доусона (того, кто написал фсупик)

Наверное еще есть сайты. Не раз натыкался на интересные вещи работая с поиском в гугле.

Единственное — они все на английском.

 

Ну и некоторые вещи (причем, думаю что важные) можно найти здесь.

 

Если вспомню или наткнусь на полезные книги — обязательно напишу.


Edited by serg_p

коммент по поводу очередности загрузки — с SimConnect может взаимодействовать и «приборная» dll, запись для нее в конфиге коннекта не нужна, записи в конфиге коннекта нужны для внешних библиотек, которые будет грузить сам коннект. Загружена же приборная dll будет при загрузке модели, тогда же будет дернут ее module_init, в котором и надо проинициализировать интерфейс с коннектом. Так что невидимый прибор — лишняя сущность

Денис, Вы, похоже, имеете в виду случай, когда в dll есть видимые приборы и они прописаны в Panel.cfg. Тут да, сим загрузит dll, прочтет Linkage, выгребет из него указатель module_init() и запустит ее. Но ведь может быть так, что в данной dll нет ни одного видимого прибора, ну, например, просто не надо. В этом случае потребуется сделать один фейковый, невидимы прибор и писать его в Panel.cfg.

 

Т.е. тут собственно вопрос, откуда сим узнает, что нужно загрузить данную dll, если из нее в Panel.cfg, запускаемой модели не прописан ни один прибор и если данная dll не прописана в DLL.xml? Тут по-любому нужен либо один фейковый прибор, что бы сим загружал dll при загрузке полета с данной моделью, либо необходимо прописать dll в DLL.xml, что бы сим ее грузил при собственном старте. Я лично других вариантов не знаю и в SDK для FSX не видел. Мож пропустил.

 

А вариант, когда нет надобности делать в dll видимые приборы — реально возможен. Я сделал для Никиты (для Ту-144) dll, код которой читает некоторые L:переменные и пишет через SimConnect в переменные некоторых топливных баков и в некоторые Payload Stations (PAYLOAD STATION WEIGHT:index). Здесь потребовалось сделать один невидимый прибор и прописать его в Panel.cfg, что бы FSX загружал dll при старте полета с Ту-144. Собственно случай dll без единого видимого прибора вполне возможен и в серьезном приборном комплексе, когда весь уровень логики сконцентрирован в одной dll, а визуализация происходит исключительно в ВК через 3D приборы (собственно идеальный вариант ВК для самолетов с будильниками), ну или посредством XML приборов.


Edited by serg_p

День добрый. Для реализации множества систем ЛА потребовалось большое количество панелей (2D). В gauges.h и мануале SDK (Event IDs) нашел использование только 9ти панелей. Подскажите пожалуйста, если возможно, как открывать и закрывать панели с номером больше [Window08]?

 

И вот еще вопрос:

trigger_key_event (KEY_PANEL_1, 0); — с помощью этой команды происходит открытие / закрытие панели №1,

trigger_key_event (KEY_PANEL_ID_OPEN, 0); — а вот здесь не могу понять как открыть нужную панель?

День добрый. Для реализации множества систем ЛА потребовалось большое количество панелей (2D). В gauges.h и мануале SDK (Event IDs) нашел использование только 9ти панелей. Подскажите пожалуйста, если возможно, как открывать и закрывать панели с номером больше [Window08]?

 

И вот еще вопрос:

trigger_key_event (KEY_PANEL_1, 0); — с помощью этой команды происходит открытие / закрытие панели №1,

trigger_key_event (KEY_PANEL_ID_OPEN, 0); — а вот здесь не могу понять как открыть нужную панель?

 

в panel.cfg создаешь к примеру

 

[Window16]

Background_color=0,0,0

size_mm=690,276

visible=0

position=0

sizeable=1

ident=10016

 

gauge00=gauge!name, 0, 0

 

в гауге

 

#define IDENT_P16 (IDENT_USER+16)

 

panel_window_open_ident(IDENT_P16);

panel_window_close_ident(IDENT_P16);


Edited by fsfreelancer

в panel.cfg создаешь к примеру

 

[Window16]

Background_color=0,0,0

size_mm=690,276

visible=0

position=0

sizeable=1

ident=10016

 

gauge00=gauge!name, 0, 0

 

в гауге

 

#define IDENT_P16 (IDENT_USER+16)

 

panel_window_open_ident(IDENT_P16);

panel_window_close_ident(IDENT_P16);

 

Огромное спасибо, Работает! Теперь когда нет ограничений в окнах, можно в проект добавить много полезного… Например надпись «пауза» в середине экрана, когда пользователь ее жмет (не всегда сразу заметно дефолтную в углу экрана).


Edited by seyco

Огромное спасибо, Работает! Теперь когда нет ограничений в окнах, можно в проект добавить много полезного… Например надпись «пауза» в середине экрана, когда пользователь ее жмет (не всегда сразу заметно дефолтную в углу экрана).

 

ага. главное не забудь ident менять в окнах, пользовательские начинаются с 10000. и еще помоему есть какое-то ограничение на количество этих окон. количество не скажу, не помню.

Ох, а что ж всё так сложно? Я так понимаю Вы пытаетесь при запуске подтянуть все файлы из какой-то конкретной sound директории? А зачем? Начните с малого, напишите инициализацию DX и чтение файлов из конктретной директории. Код уже есть. Я, к сожалению, с десяткой знаком поверхностно, но инициализация гауг там аналогична девятке, поэтому советую Вам обратить внимание на функции module_init и module_deinit, в них Вам и надо инициализировать/деинициализировать DX (для этого так же надо будет поковырять Linkage модуля, но это здесь уже обсуждалось тыщу раз). Далее, загружать все файлы сразу я бы не стал, я бы загрузку сделал on demand, т.е. если файл нужен — загрузили, оставили его в списке и делаем с ним всё что надо. Если какой-либо файл никогда не проиграется, то и в памяти ему делать нечего. Приведи пример из нашего тамагочи, надеюсь поможет:

 

Инициализация интерфейса:

HWND hwndSim;
HINSTANCE hInstanceSim;

BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserve){
  hwndSim = FindWindow(L"FS98MAIN", NULL);
  hInstanceSim = (HINSTANCE)hModule;
  return TRUE;
}

void FSAPI module_init(void){
  soundResMgr = new CSoundResMgr();
  if(!soundResMgr->Init(hwndSim))
    MessageBox(NULL, L"Unable to initialize sound system", L"Tu2X4-Error", MB_OK);
}

Заголовок «библиотеки»:

#ifndef SOUND_RES_MGR
#define SOUND_RES_MGR

#include <dsound.h>
#include <mmsystem.h>

#define MIN_SOUND_VOL       -10000
#define MAX_SOUND_VOL       -3000
#define EXT_VIEW_VOL_DISP   3000

class CSoundResMgr {
  public:
    bool Init(HWND hWnd);
    void DeInit();
    const HWND GetModuleWnd(){return hAirplaneWnd;}
    LPDIRECTSOUND GetDirectSoundInf(){return lpDirectSound;}
  protected:
    HWND hAirplaneWnd;
    LPDIRECTSOUND lpDirectSound;
};

class CSoundFile{
  public:
    CSoundFile(CSoundResMgr* soundresmgr){dsbSound = NULL;soundResMgr = soundresmgr;}
    ~CSoundFile(){if (dsbSound != NULL) { dsbSound->Release(); dsbSound = NULL; }}
    bool LoadFromFile(char* filename);
    void PlaySoundNow(bool looped = false);
    void StopSound();
    void SetVolume(LONG volume);
    bool IsPlaying();
    bool IsNearEnd();
  protected:
    CSoundResMgr* soundResMgr;
    LPDIRECTSOUNDBUFFER dsbSound;
    DWORD soundCreationOptions;
    DWORD nearEndBorder;
};

#endif

Код «библиотеки»:

#include "SoundResMgr.h"

bool CSoundResMgr::Init(HWND hWnd){
  hAirplaneWnd = hWnd;
  if(DirectSoundCreate (NULL, &lpDirectSound, NULL) != DS_OK) return false;
  if(lpDirectSound->SetCooperativeLevel (hAirplaneWnd, DSSCL_NORMAL) != DS_OK) return false;
  return true;
}

void CSoundResMgr::DeInit(){
  if (lpDirectSound) { lpDirectSound->Release(); lpDirectSound = NULL; }
}

bool CSoundFile::LoadFromFile(char* filename){
  HMMIO wavefile;
  wavefile = mmioOpenA (filename, 0, MMIO_READ | MMIO_ALLOCBUF);

  if (wavefile == NULL) return false;

  soundCreationOptions = DSBCAPS_CTRLVOLUME | DSBCAPS_GETCURRENTPOSITION2;

  MMCKINFO parent;
  memset (&parent, 0, sizeof(MMCKINFO));
  parent.fccType = mmioFOURCC ('W', 'A', 'V', 'E');
  mmioDescend (wavefile, &parent, 0, MMIO_FINDRIFF);

  MMCKINFO child;
  memset (&child, 0, sizeof(MMCKINFO));
  child.fccType = mmioFOURCC ('f', 'm', 't', ' ');
  mmioDescend (wavefile, &child, &parent, 0);

  WAVEFORMATEX wavefmt;
  mmioRead (wavefile, (char*)&wavefmt, sizeof(wavefmt));

  if(wavefmt.wFormatTag != WAVE_FORMAT_PCM) return false;

  mmioAscend (wavefile, &child, 0);
  child.ckid = mmioFOURCC ('d', 'a', 't', 'a');
  mmioDescend (wavefile, &child, &parent, MMIO_FINDCHUNK);

  //soundDuration = (DWORD)(1000 * child.cksize / wavefmt.nAvgBytesPerSec);
  nearEndBorder = child.cksize - (DWORD)(wavefmt.nAvgBytesPerSec*1.5);

  DSBUFFERDESC bufdesc;
  memset (&bufdesc, 0, sizeof(DSBUFFERDESC));
  bufdesc.dwSize = sizeof(DSBUFFERDESC);

  bufdesc.dwFlags = soundCreationOptions;
  bufdesc.dwBufferBytes = child.cksize;
  bufdesc.lpwfxFormat = &wavefmt;

  if((soundResMgr!= NULL) && (soundResMgr->GetDirectSoundInf()->CreateSoundBuffer (&bufdesc, &dsbSound, NULL))!= DS_OK) return false;

  void *write1 = 0, *write2 = 0;
  unsigned long length1,length2;
  dsbSound->Lock (0, child.cksize, &write1, &length1, &write2, &length2, 0);
  if(write1 > 0)
  mmioRead (wavefile, (char*)write1, length1);
  if (write2 > 0)
  mmioRead (wavefile, (char*)write2, length2);
  dsbSound->Unlock (write1, length1, write2, length2);

  mmioClose (wavefile, 0);

  return true;
}

void CSoundFile::PlaySoundNow(bool looped){
  DWORD dwLooping = looped ? DSBPLAY_LOOPING : 0;
  if(dsbSound){ 
    if(!IsPlaying()){
      dsbSound->SetCurrentPosition(0);
      dsbSound->Play(0, 0, dwLooping);
    }
  }
}

void CSoundFile::StopSound(){
  if(dsbSound) dsbSound->Stop();
}

void CSoundFile::SetVolume(LONG volume){
  if((dsbSound) && (soundCreationOptions & DSBCAPS_CTRLVOLUME)){
    if(volume < MIN_SOUND_VOL) dsbSound->SetVolume(MIN_SOUND_VOL);
    else if(volume > MAX_SOUND_VOL) dsbSound->SetVolume(MAX_SOUND_VOL);
    else dsbSound->SetVolume(volume);
  }
}

bool CSoundFile::IsPlaying(){
  DWORD dwStatus = 0;
  bool isplaying = false;
  if(dsbSound){
    dsbSound->GetStatus(&dwStatus);
    isplaying |= ( ( dwStatus & DSBSTATUS_PLAYING ) != 0 );
  }
  return isplaying;
}

bool CSoundFile::IsNearEnd(){
  bool isnearend = false;
  DWORD curPos = 0;
  if(dsbSound){
    dsbSound->GetCurrentPosition(&curPos, NULL);
    if(curPos >= nearEndBorder) return true;
  }
  return isnearend;
}

Смысл всего этого в том, что есть класс CSoundFile, который оборачивает один конкретный wav файл. И есть менеджер класс, отвечающий за инициализацию и использование DirectSound. Загрузка и использование файла идёт через CSoundFile::LoadFile

 

Учтите, это всё для девятки (это важно для инициализации, под десятку возможно надо будет малость подправить). И ессно там только те функции, которые нужны были мне для тамагочи, DirectSound может намного больше. Например я сделал у нас адаптацию громкости звука в зависимости от положения наблюдателя (ВСУ например в кабине и ВК еле слышно, а при виде снаружи громкость обычная).

Мои под утренние отчёты становятся традицией ) 

 

Огромное спасибо за пример!

 

Почти во всём разобрался. Собрал библиотеку. «Щупом»  MessageBox определил что пустой прибор, без .wav файлов, успешно загружается. MessageBox приветливо рапортует о том что wavefile == пуст. Как только прописываю путь к какому-нибудь звуковому файлу (к слову, я явно не так делаю, наверняка есть способ практичнее, попробую организовать запись в буфер из ресурса), предварительно вписав LoadFromFile метод в void FSAPI module_init(void) — FSX выдаёт фатальную ошибку. С чем это может быть связано?

 

Спасибо большое.

Почти во всём разобрался. Собрал библиотеку. «Щупом»  MessageBox определил что пустой прибор, без .wav файлов, успешно загружается. MessageBox приветливо рапортует о том что wavefile == пуст. Как только прописываю путь к какому-нибудь звуковому файлу (к слову, я явно не так делаю, наверняка есть способ практичнее, попробую организовать запись в буфер из ресурса), предварительно вписав LoadFromFile метод в void FSAPI module_init(void) — FSX выдаёт фатальную ошибку. С чем это может быть связано?

 

Спасибо большое.

 

Это надо в код гауги смотреть, так по фотографии я мало что могу определить. По поводу загрузки файла я свехру дал полностью рабочий код. Возьмите CSoundFile::LoadFromFile и сделайте как отдельную функцию. Не используйте ресурсы, хотя бы потому что потом у пользователей Вашей гауги будет возможность менять звуковые файлы (подходит для озвучки например).  Да и от WAV файлов сам файл гауги распухнет. Ну это конечно всё если хранение звука в ресурсе не самоцель.

 

Ещё, как насчёт нормального дебагера вместо MessageBox? В десятке это также работает, как и в девятке, Вы же свою сборку делаете в дебаг версии? Тогда Вам ясно будет видно, где что валится.

 

И вообще где наши спецы по десятке? То их как песка в море, то все по окопам? Я не знаю, мож там какая особеность есть, которой нет в девятке.

Спасибо! По видимому, после ухода Сергея из FSX, специалистов 10-и не осталось.
Ваш код я не трогал, оставил всё как есть, лишь вызов некоторых функций происходит через класс созданный по принципам описанным serg_p. Вот, к примеру, как выполнена тестовая загрузка файла:

void LogicType::init(void) //Class initialization method in void FSAPI module_init(void)
{
LPSTR testWav = "\test.wav";
testS.LoadFromFile(testWav);
}

А дебагером я проходил, только вот толку!? Ошибки я там не вижу.
Ругается так:

Unhandled exception at 0x58c83818 (test_v43.dll) in fsx.exe: 0xC0000005:Access violation reading location 0x00000000.

и

dword CXX0017: Error: symbol «dword» not found

Тычет сюда

dsbSound->Lock(0 child.cksize, &write1, &length1, &write2, &length2, 0);
Spoiler

 
К слову, достал обновлённую версию брошюры Dai Griffiths. По звуку там чуть-чуть, но есть пример использования DirectSound в приборах с исходным кодом. Так вот, тестовый прибор из примера, у меня отказался запускаться.
Я начинаю думать, может у меня DirectX какой-то не такой =) !? 11 стоит.
 
Спасибо )

Возможно что дело в версии DX, здесь у меня просто уже опыта не так много, как хотелось бы. Я так понимаю, у Вас SDK 11-й версии?

Почитайте вот это http://www.codeguru….DirectSound.htm , правда там малость другой подход.

Е-е-е-е-е! Заиграл родимый! =)
icebear, спасибо большое.
Пользовался вот этим руководством, может кому-нибудь пригодится, там и исходные коды с примером есть.
Завтра всё причешу под FSX, пока есть затруднения с проигрыванием лишь при изменении значения переменной. Ни как не могу «проиграть» функцию без участия внутреннего таймера FSX.

Всем ещё раз большое спасибо!

Ни как не могу «проиграть» функцию без участия внутреннего таймера FSX.

Это как понять? И ещё, Вы пишите гаугу «по старинке» или с симконнектом? Просто с симконнектом руки у Вас сильно развязаны и возможностей море.

Это как понять?

В смысле делаю так:

Spoiler

И ещё, Вы пишите гаугу «по старинке» или с симконнектом?

Всё организовано с помощью gauges.h, так как, на сколько я понял, SimConnect’а может быть целых три версии — отсюда могут возникнуть проблемы с совместимостью. И основное, мне в руке пока не попадались хоть какие-нибудь примеры динамических библиотек использующих исключительно SimConnect. Модули — да, но это не то.

Спасибо =)

UPD

Spoiler

Такой вариант так же не приводит к желаемому результату.

«Не, ну в таких условиях я не могу работать» :)

Условие проигрывания отличается от того, что было пару сообщение назад. Вам нужна не простоя смена состояния, а конкретное, т.е. с одного конретного значение на другое. Попробуйте так:

if((gen_mast != gen_mast_st) && (gen_mast == 0)){
m_Sound->PlayNow();
}

gen_mast_st = gen_mast;

Обратите внимание, присвоение идёт в каждой итерации цикла гауги, теперь оно вне скобок, это важный момент.

Люди добрые, подскажите кто сможите.
Остались ли тут грамотные специалисты по созданию приборов на с++? Есть некий проект. Но очень не хватает программеров. Если есть желание помочь, пишите в лс.

Как лучше организовать обмен данными между С++ гагами в FSX?

Короче, указатели на структуры треба передавать в другую гагу.

Имхуется мне, разные гаги имеют разные адресные пространства и даже если напрямую передам указатель, н-р через XML-переменную, смысла не имеет.

Это очень хорошо. Просто у Гриффитса написано в разделе «Runnig Visual C++ as a Gauge Debugger»:

This section has been deprecated as both FS9 and FSX will refuse to start if you attach a debugger to them. Microsoft giveth and Microsoft taketh away; they‟ve considerably improved the SDK for FS9 and FSX but have made it far more difficult to debug the gauges you create.

Что за Гриффитс? Не гуглится.

Где это скачать можно?

 

icebear

2Krit
Крайняя версия:
http://www.flightsim…archid=36032586

2Krit
Крайняя версия:
http://www.flightsim…archid=36032586

Не понял, пустая страница открывается:

 

No Search Results

No Files matched your search criteria.

 

Вопрос по топливной системе: как добраться до баков EXTERNAL1 и EXTERNAL2 (FS9)?

Суть вопроса: для начала хочу просто считать количество топлива в баке (в Aircraft.cfg баки, естественно, заданы). Для всех остальных типов баков в GAUGES.H определены соответствующие токены, а для внешних — ничего не вижу. Токены FUEL_QUANTITY_RIGHT и FUEL_QUANTITY_LEFT показывают лишь суммы MAIN+AUX+TIP.

Поиск дал три зацепки:
1. Вот этот пост утверждает, что соответствующие токены ( A:FUEL TANK External1/External2 QUANTITY ) имеют место быть. И, действительно, в файле «FS2004_Value_Variables.txt» от http://www.FS2x.com такие токены есть. Более того, проверил в xml-приборе — токены работают. Но мне надо бы на С.
2. В модели Il-18 вроде бы реализован необходимый функционал. По крайней мере, соответствующий прибор панели показывает количество топлива в этих баках.
3. Токены определены и в SDK для АЫЧ. Но мне надо в FS9. Попытка самостоятельно прописать эти токены в GAUGES.H, естественно, успеха не имела.

Можно, конечно, пробовать извращаться с чтением токенов через L-переменные из xml-файла, но ведь должен быть более простой путь. Что и где я упустил?

Предыдущий вопрос порешил через дополнение GAUGES.H структурой XML_TOKEN. А вот как можно добраться до баков MAIN? В структуре FUEL_SELECT я этих баков не наблюдаю (видимо, кто-то в Microsoft решил «сэкономить» пару байтов). Пока могу добраться до них лишь опосредованно, через использование FUEL_TANK_SELECTOR_LEFT/RIGHT при уже пустых баках AUX и TIP. Но это явный костыль (к тому же, не везде работающий). Может, есть какой-то иной способ?