Advertisement
Guest User

mxINI v0.5 by MX_Master - удалена лишняя константа INI_DEAULT_VALUE_SIZE

a guest
Sep 20th, 2010
6,293
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Pawn 100.16 KB | None | 0 0
  1. /*
  2.     ! file encoding: Windows-1251
  3.  
  4.  
  5.    ------------------------------------------
  6.  
  7.     Самый быстрый INI ридер/райтер ( mxINI )
  8.  
  9.    ------------------
  10.  
  11.  
  12.     ЧТО ЭТО
  13.         - Это инклуд-файл для PAWN скриптов мультиплеера GTA San Andreas (SA-MP)
  14.  
  15.     КАК ИСПОЛЬЗОВАТЬ
  16.         - Поместить этот файл в папку include, которая лежит рядом с компилятором
  17.         - В самом верху вашего скрипта добавить #include <mxINI.inc>
  18.  
  19.     АВТОР
  20.          MX_Master
  21.  
  22.     ВЕРСИЯ
  23.         0.5 от 20.09.2010
  24.  
  25.  
  26.     ДОСТУПНЫЕ ИНСТРУМЕНТЫ
  27.     более подробные описания даны перед каждой функцией ниже в коде
  28.  
  29.         ini_createFile ( "путь/к/файлу.ini", "Содержимое файла по умолчанию" )
  30.  
  31.         ini_openFile ( "путь/к/файлу.ini" )
  32.  
  33.         ini_closeFile ( ИД_открытого_файла )
  34.     --
  35.         ini_setString ( ИД_открытого_файла, "имя ключа", "текстовое значение" )
  36.  
  37.         ini_setInteger ( ИД_открытого_файла, "имя ключа", 123456 )
  38.  
  39.         ini_setFloat ( ИД_открытого_файла, "имя ключа", 3.1416 )
  40.     --
  41.         ini_getString ( ИД_открытого_файла, "имя ключа", returnValue )
  42.  
  43.         ini_getInteger ( ИД_открытого_файла, "имя ключа", returnValue )
  44.  
  45.         ini_getFloat ( ИД_открытого_файла, "имя ключа", returnValue )
  46.     --
  47.         ini_removeKey ( ИД_открытого_файла, "имя ключа" )
  48.  
  49.         ini_getErrorInfo ( Код_ошибки )
  50.  
  51.  
  52.     ПРИМЕРЫ
  53.  
  54.         new iniFile = ini_createFile ( "test4268.ini" );
  55.  
  56.         if ( iniFile < 0 )
  57.             iniFile = ini_openFile ( "test4268.ini" );
  58.  
  59.         if ( iniFile >= 0 )
  60.         {
  61.             new returnString[32], returnNumber, Float: returnFloat;
  62.  
  63.             ini_setString  ( iniFile, "ключ со строкой", "текстовое значение" );
  64.             ini_setInteger ( iniFile, "ключ с числом",   123456 );
  65.             ini_setFloat   ( iniFile, "ключ с дробью",   3.1416 );
  66.  
  67.             ini_removeKey  ( iniFile, "ключ с числом" );
  68.  
  69.             ini_getString  ( iniFile, "ключ со строкой", returnString );
  70.             ini_getInteger ( iniFile, "ключ с числом",   returnNumber );
  71.             ini_getFloat   ( iniFile, "ключ с дробью",   returnFloat );
  72.  
  73.  
  74.             ini_closeFile  ( iniFile );
  75.  
  76.             printf ( "\n `ключ_со_строкой` = `%s`,\n `ключ_с_числом` = `%d`,\n `ключ_с_дробью` = `%f` \n",
  77.                 returnString, returnNumber, returnFloat );
  78.         }
  79.         else print( "\n Не удалось открыть INI файл \n" );
  80. */
  81.  
  82.  
  83.  
  84.  
  85.  
  86.  
  87.  
  88.  
  89.  
  90.  
  91.  
  92. const
  93.  
  94.     // настройки
  95.  
  96.     // РЕКОМЕНДУЕТСЯ ИЗМЕНИТЬ ПОД СЕБЯ
  97.     INI_MAX_OPENED_FILES    = 2,        // максимум, открытых одновременно, файлов
  98.     INI_MAX_FILE_SIZE       = 65536,    // байт,        макс. размер файла
  99.     INI_MAX_FILENAME_SIZE   = 128,      // символов,    макс. размер пути к файлу
  100.     INI_MAX_KEYS_IN_FILE    = 2048,     // максимум ключей в открытом файле
  101.  
  102.     // в ОЗУ будет выделено для временного хранения файлов примерно вот столько байт:
  103.     // INI_MAX_OPENED_FILES * ( INI_MAX_FILE_SIZE + INI_MAX_FILENAME_SIZE*4 + INI_MAX_KEYS_IN_FILE*4 )
  104.  
  105.  
  106.     // НЕЛЬЗЯ МЕНЯТЬ
  107.     INI_INTEGER_SIZE        = 12,       // размер строки с целочисленным значением
  108.     INI_FLOAT_SIZE          = 40,       // размер строки с дробным числовым значением
  109.  
  110.     INI_STRING_DELIMITER    = '\n',     // разделитель строк
  111.     INI_DELIMITER           = '=',      // разделитель ключа и значения
  112.  
  113.  
  114.     // коды ошибок, возвращаемые функциями
  115.  
  116.     // РЕКОМЕНДУЕТСЯ НЕ ИЗМЕНЯТЬ
  117.     INI_OK                  =  0,       // функция успешно выполнена
  118.  
  119.         // проверять на ошибку можно так:
  120.         // if ( возвращаемое_значение_функции < 0 ) ...
  121.  
  122.     INI_FILE_NOT_FOUND      = -1,       // файл не найден по указанному пути
  123.     INI_FILE_ALREADY_EXIST  = -2,       // файл не найден по указанному пути
  124.     INI_TOO_LARGE_FILE      = -3,       // размер файла превысил допустимый лимит
  125.     INI_WRONG_PATH_SIZE     = -4,       // неправильный размер пути к файлу
  126.     INI_READ_ERROR          = -5,       // ошибка чтения файла
  127.     INI_WRITE_ERROR         = -6,       // ошибка при записи в файл
  128.     INI_NO_FREE_SLOT        = -7,       // нет свободного слота для открытия файла
  129.     INI_WRONG_SLOT          = -8,       // указан неверный слот открытого файла
  130.     INI_KEY_NOT_FOUND       = -9,       // ключ в открытом файле не найден
  131.     INI_WRONG_RETURN_SIZE   = -10,      // размер строки, в которую будет помещено значение ключа - указан неверно (<= 0)
  132.  
  133.  
  134.     // другие вспомогательные константы
  135.  
  136.     // НЕЛЬЗЯ МЕНЯТЬ
  137.     cellbytes = cellbits / charbits;    // кол-во байт в одной ячейке
  138.  
  139.  
  140.  
  141.  
  142.  
  143. // списки разных символов для оператора case в одной из функций
  144.  
  145. // РЕКОМЕНДУЕТСЯ НЕ ИЗМЕНЯТЬ
  146. #define INI_SPACE_CHAR  ' ', '\t'                       // строковые пробельные символы
  147. #define INI_KEY_STARTS  ' ', '\t', '\r', '\n', '\0'     // символы, перед началом ключа
  148. #define INI_STRING_ENDS '\r', '\n', '\0'                // символы, завершающие значение
  149. #define INI_NUMBER_ENDS ' ', '\t', '\r', '\n', '\0'     // символы, завершающие численное значение
  150.  
  151.  
  152.  
  153.  
  154.  
  155. static stock
  156.  
  157.     // временное хранилище открытых файлов
  158.  
  159.     _ini_nSlotUsed      [ INI_MAX_OPENED_FILES ],                               // флаг: занят ли слот
  160.     _ini_nFileChanged   [ INI_MAX_OPENED_FILES ],                               // флаг: был ли изменен файл
  161.     _ini_nFileBusy      [ INI_MAX_OPENED_FILES ],                               // флаг: изменяется ли в данный момент содержимое файла
  162.     _ini_nFileSize      [ INI_MAX_OPENED_FILES ],                               // размер открытого файла
  163.     _ini_nDelimPos      [ INI_MAX_OPENED_FILES ] [ INI_MAX_KEYS_IN_FILE ],      // список позиций INI_DELIMITER
  164.     _ini_nKeysCount     [ INI_MAX_OPENED_FILES ],                               // кол-во ключей открытого файла
  165.     _ini_szFilePath     [ INI_MAX_OPENED_FILES ] [ INI_MAX_FILENAME_SIZE ],     // путь к файлу
  166.     _ini_szFileContent  [ INI_MAX_OPENED_FILES ] [ INI_MAX_FILE_SIZE char ];    // контент файла
  167.  
  168.  
  169.  
  170.  
  171.  
  172.  
  173.  
  174.  
  175.  
  176.  
  177.  
  178.  
  179.  
  180.  
  181.  
  182.  
  183.  
  184.  
  185.  
  186.  
  187.  
  188. /*
  189.     Создает и сразу открывает INI файл для чтения/записи.
  190.  
  191.     ПОДРОБНЕЕ
  192.         Файл создается только в ОЗУ, и в него записывается строка szDefaultContent.
  193.         В szDefaultContent могут быть и ключи, которые потом парсер также будет видеть.
  194.         Только при закрытии файла, его содержимое будет записано на диск
  195.         по указанному пути к файлу.
  196.  
  197.     ПАРАМЕТРЫ:
  198.         szFilePath[]        путь к файлу
  199.         szDefaultContent    контент файла по умолчанию, обычно, можно не указывать
  200.  
  201.     ВЕРНЕТ:
  202.         код ошибки < 0 или ИД_открытого_файла
  203. */
  204.  
  205. stock ini_createFile ( szFilePath[], szDefaultContent[] = "" )
  206. {
  207.     //
  208.     //  несколько блоков с проверками
  209.     //
  210.  
  211.     // ------------
  212.     new nFileNameSize = strlen( szFilePath ); // узнаем размер пути
  213.  
  214.     // если размер пути неправильный
  215.     if ( nFileNameSize <= 0 || nFileNameSize >= INI_MAX_FILENAME_SIZE )
  216.         return INI_WRONG_PATH_SIZE; // вернем код ошибки
  217.     // ------------
  218.  
  219.  
  220.     // ------------
  221.     if ( fexist( szFilePath ) ) // если файл уже существует
  222.         return INI_FILE_ALREADY_EXIST; // вернем код ошибки - файл уже существует
  223.  
  224.     for ( new slot = 0; slot < INI_MAX_OPENED_FILES; slot++ ) // перебор всех слотов ОЗУ
  225.         if // если уже есть такой открытый файл
  226.         (
  227.                 _ini_nSlotUsed[slot] != 0
  228.             &&  strcmp( szFilePath, _ini_szFilePath[slot], false ) == 0
  229.         )
  230.             return INI_FILE_ALREADY_EXIST; // вернем код ошибки - файл уже существует
  231.     // ------------
  232.  
  233.  
  234.  
  235.     // ------------
  236.     new nFileSize = strlen( szDefaultContent ); // узнаем размер контентa файла по умолчанию
  237.  
  238.     // если размер неправильный
  239.     if ( nFileSize < 0 || nFileSize >= INI_MAX_FILE_SIZE )
  240.         return INI_TOO_LARGE_FILE; // вернем код ошибки
  241.     // ------------
  242.  
  243.  
  244.  
  245.  
  246.     //
  247.     //  поиск свободного слота для записи в ОЗУ
  248.     //
  249.  
  250.     for ( new slot = 0; slot < INI_MAX_OPENED_FILES; slot++ ) // перебор всех слотов ОЗУ
  251.     {
  252.         if ( _ini_nSlotUsed[slot] != 1 ) // если слот найден
  253.         {
  254.             // ------------
  255.             _ini_nSlotUsed[slot]        = 1; // застолбим найденное место
  256.             _ini_nFileChanged[slot]     = 0; // файл не был изменен
  257.             _ini_nFileSize[slot]        = nFileSize; // скопируем размер файла в слот
  258.             _ini_nKeysCount[slot]       = 0; // кол-во ключей выставим 0
  259.             // ------------
  260.  
  261.  
  262.             // ------------
  263.             // скопируем в ОЗУ весь файл,
  264.             // запомнив все позиции INI_DELIMITER и их кол-во
  265.             for ( new i = 0;  i < nFileSize && i < INI_MAX_FILE_SIZE;  i++ )
  266.             {
  267.                 if (
  268.                         szDefaultContent[i] == INI_DELIMITER // если символ это INI_DELIMITER
  269.                     &&  _ini_nKeysCount[slot] < INI_MAX_KEYS_IN_FILE // и лимит ключей еще не исчерпан
  270.                 ) {
  271.                     _ini_nDelimPos[slot][ _ini_nKeysCount[slot] ] = i; // добавим еще одну позицию INI_DELIMITER
  272.                     _ini_nKeysCount[slot]++; // увеличим кол-во найденных ключей
  273.                 }
  274.  
  275.                 _ini_szFileContent[slot]{i} = szDefaultContent[i];
  276.             }
  277.  
  278.             _ini_szFileContent[slot]{nFileSize} = 0; // символ конца строки для контента
  279.             // ------------
  280.  
  281.  
  282.             // ------------
  283.             // скопируем в озу путь к файлу
  284.             memcpy( _ini_szFilePath[slot], szFilePath, 0, nFileNameSize * cellbytes );
  285.  
  286.             _ini_szFilePath[slot][nFileNameSize] = 0; // символ конца строки для пути
  287.             // ------------
  288.  
  289.  
  290.             return slot; // вернем ИД слота
  291.         }
  292.     }
  293.  
  294.  
  295.  
  296.  
  297.     //
  298.     //  если свободный слот в памяти не найден
  299.     //
  300.  
  301.     return INI_NO_FREE_SLOT; // вернем код ошибки
  302.     // ------------
  303. }
  304.  
  305.  
  306.  
  307.  
  308. /*
  309.     Открывает INI файл для чтения/записи, если он существует.
  310.  
  311.     ПОДРОБНЕЕ
  312.         Копирует в ОЗУ с диска всё содержимое файла, если его размер не превышает
  313.         допустимый. Если кол-во ключей в файле больше допустимого, ошибки никакой не будет,
  314.         просто при чтении/записи значений, парсер не будет видеть лишние ключи.
  315.  
  316.     ПАРАМЕТРЫ:
  317.         szFilePath[]    путь к файлу
  318.  
  319.     ВЕРНЕТ:
  320.         код ошибки < 0 или ИД_открытого_файла
  321. */
  322.  
  323. stock ini_openFile ( szFilePath[] )
  324. {
  325.     //
  326.     //  несколько блоков с проверками
  327.     //
  328.  
  329.     // ------------
  330.     new nFileNameSize = strlen( szFilePath ); // узнаем размер пути
  331.  
  332.     // если размер пути неправильный
  333.     if ( nFileNameSize <= 0 || nFileNameSize >= INI_MAX_FILENAME_SIZE )
  334.         return INI_WRONG_PATH_SIZE; // вернем код ошибки
  335.     // ------------
  336.  
  337.  
  338.     if ( ! fexist( szFilePath ) ) // если файл не найден
  339.         return INI_FILE_NOT_FOUND; // вернем код ошибки
  340.  
  341.  
  342.     // ------------
  343.     // проверка - открыт ли уже файл с таким именем
  344.     for ( new slot = 0; slot < INI_MAX_OPENED_FILES; slot++ ) // перебор всех слотов ОЗУ
  345.         if // если уже есть такой открытый файл
  346.         (
  347.                 _ini_nSlotUsed[slot] != 0
  348.             &&  strcmp( szFilePath, _ini_szFilePath[slot], false ) == 0
  349.         )
  350.             return slot; // просто вернем его слот
  351.     // ------------
  352.  
  353.  
  354.     // ------------
  355.     new File: pFile = fopen( szFilePath, io_read ); // пытаемся открыть файл для чтения
  356.  
  357.     if ( ! pFile ) // если файл не открылся
  358.         return INI_READ_ERROR; // вернем код ошибки
  359.     // ------------
  360.  
  361.  
  362.     // ------------
  363.     new nFileSize = flength( pFile ); // узнаем размер файла
  364.  
  365.     if ( nFileSize >= INI_MAX_FILE_SIZE ) // если размер файла слишком большой
  366.     {
  367.         fclose(pFile); // закроем файл
  368.         return INI_TOO_LARGE_FILE; // вернем код ошибки
  369.     }
  370.     // ------------
  371.  
  372.  
  373.  
  374.  
  375.     //
  376.     //  поиск свободного слота для записи в ОЗУ
  377.     //
  378.  
  379.     // ------------
  380.     for ( new slot = 0; slot < INI_MAX_OPENED_FILES; slot++ ) // перебор всех слотов ОЗУ
  381.     {
  382.         if ( _ini_nSlotUsed[slot] != 1 ) // если слот найден
  383.         {
  384.             // ------------
  385.             _ini_nSlotUsed[slot]        = 1; // застолбим найденное место
  386.             _ini_nFileChanged[slot]     = 0; // файл не был изменен
  387.             _ini_nFileSize[slot]        = nFileSize; // скопируем размер файла в слот
  388.             _ini_nKeysCount[slot]       = 0; // кол-во ключей выставим 0
  389.             // ------------
  390.  
  391.  
  392.             // ------------
  393.             // скопируем в ОЗУ весь файл,
  394.             // запомнив все позиции INI_DELIMITER и их кол-во
  395.             for ( new i = 0, symbol; i < nFileSize && i < INI_MAX_FILE_SIZE; i++ )
  396.             {
  397.                 symbol = fgetchar( pFile, 0, false ); // читаем из файла следующий символ
  398.  
  399.                 if (
  400.                         symbol == INI_DELIMITER // если символ это INI_DELIMITER
  401.                     &&  _ini_nKeysCount[slot] < INI_MAX_KEYS_IN_FILE // и лимит ключей еще не исчерпан
  402.                 ) {
  403.                     _ini_nDelimPos[slot][ _ini_nKeysCount[slot] ] = i; // добавим еще одну позицию INI_DELIMITER
  404.                     _ini_nKeysCount[slot]++; // увеличим кол-во найденных ключей
  405.                 }
  406.  
  407.                 _ini_szFileContent[slot]{i} = symbol;
  408.             }
  409.  
  410.             _ini_szFileContent[slot]{nFileSize} = 0; // символ конца строки для контента
  411.  
  412.             fclose(pFile); // закроем файл
  413.             // ------------
  414.  
  415.  
  416.             // ------------
  417.             // скопируем в озу путь к файлу
  418.             memcpy( _ini_szFilePath[slot], szFilePath, 0, nFileNameSize * cellbytes );
  419.  
  420.             _ini_szFilePath[slot][nFileNameSize] = 0; // символ конца строки для пути
  421.             // ------------
  422.  
  423.  
  424.             return slot; // вернем ИД слота
  425.         }
  426.     }
  427.     // ------------
  428.  
  429.  
  430.  
  431.  
  432.     //
  433.     //  если свободный слот в памяти не найден
  434.     //
  435.  
  436.     // ------------
  437.     fclose(pFile); // закроем файл
  438.  
  439.     return INI_NO_FREE_SLOT; // вернем код ошибки
  440.     // ------------
  441. }
  442.  
  443.  
  444.  
  445.  
  446. /*
  447.     Закрывает INI файл, если он был открыт.
  448.  
  449.     ПОДРОБНЕЕ
  450.         Если файл не был изменен - освободит слот для хранения файла в ОЗУ.
  451.         Если файл был изменен - полностью перезапишет файл на диске.
  452.  
  453.     ПАРАМЕТРЫ:
  454.         nFilePointer    ИД_открытого_файла, полученный от ini_openFile / ini_createFile
  455.  
  456.     ВЕРНЕТ:
  457.         код ошибки < 0 или 0 при успехе
  458. */
  459.  
  460. stock ini_closeFile ( nFilePointer )
  461. {
  462.     if
  463.     (   // если ИД открытого файла указан верно
  464.             nFilePointer >= 0
  465.         &&  nFilePointer < INI_MAX_OPENED_FILES
  466.         &&  _ini_nSlotUsed[nFilePointer] != 0
  467.     )
  468.     {
  469.         if ( _ini_nFileChanged[nFilePointer] != 0 ) // если файл был изменен
  470.         {
  471.             new File: pFile = fopen( _ini_szFilePath[nFilePointer], io_write ); // пытаемся открыть файл для записи
  472.  
  473.             if ( ! pFile ) // если файл не открылся
  474.                 return INI_WRITE_ERROR; // вернем код ошибки
  475.  
  476.             // запишем контент файла из ОЗУ на диск
  477.             for ( new i = 0;  i < _ini_nFileSize[nFilePointer];  i++ )
  478.                 fputchar( pFile, _ini_szFileContent[nFilePointer]{i}, false );
  479.  
  480.             fclose(pFile); // закроем файл
  481.         }
  482.  
  483.         _ini_nSlotUsed[nFilePointer] = 0; // освободить слот открытого файла
  484.  
  485.         return INI_OK; // вернуть код об успешном выполнении функции
  486.     }
  487.  
  488.     return INI_WRONG_SLOT; // вернуть код: неверный указатель на открытый файл
  489. }
  490.  
  491.  
  492.  
  493.  
  494.  
  495.  
  496.  
  497.  
  498.  
  499.  
  500.  
  501. /*
  502.     Получает из открытого INI файла значение указанного ключа.
  503.  
  504.     ПОДРОБНЕЕ
  505.         Парсер ищет в ОЗУ в контенте файла указанный ключ и помещает в
  506.         szReturnValue его строковое значение. szReturnValue нужно создать заранее.
  507.  
  508.         nSizeOfReturn можно не указывать, если только не нужно точное кол-во
  509.         возвращаемых символов в строке (в описании этого параметра ниже даны
  510.         дополнительные указания).
  511.  
  512.         Как ключ так и значение в файле, могут быть любой длины и
  513.         могут содержать любые символы, кроме 2 символов конца строки \r и \n.
  514.  
  515.         Парсер не видит пробелы и знаки табуляции вокруг имени ключа и
  516.         перед значением, он считает их отступами.
  517.  
  518.     ПАРАМЕТРЫ:
  519.         nFilePointer        ИД_открытого_файла, полученный от ini_openFile / ini_createFile
  520.         szKeyName[]         имя ключа
  521.         szReturnValue[]     сюда будет помещено значение ключа в виде строки
  522.         nSizeOfReturn       макс. размер возвращаемой строки, обычно, это размер szReturnValue.
  523.                             если ваша строка szReturnValue является частью массива, который был создан с помощью enum,
  524.                             этот параметр нужно обязательно указывать как число
  525.  
  526.     ВЕРНЕТ:
  527.         код ошибки < 0 или 0 при успехе
  528. */
  529.  
  530. stock ini_getString ( nFilePointer, szKeyName[], szReturnValue[], nSizeOfReturn = sizeof(szReturnValue) )
  531. {
  532.     // ----------------
  533.     if // если ИД открытого файла указан неверно
  534.     (
  535.             nFilePointer < 0
  536.         ||  nFilePointer >= INI_MAX_OPENED_FILES
  537.         ||  _ini_nSlotUsed[nFilePointer] != 1
  538.     )
  539.         return INI_WRONG_SLOT; // вернуть код: неверный указатель на открытый файл
  540.     // ----------------
  541.  
  542.  
  543.     if ( nSizeOfReturn <= 0 ) // если по какой-то причине размер возвращаемого значения указан/рассчитан как 0
  544.         return INI_WRONG_RETURN_SIZE;
  545.  
  546.  
  547.     // ----------------
  548.     new nKeyLen = strlen(szKeyName); // узнаем длину имени указанного ключа
  549.  
  550.     if ( nKeyLen <= 0 ) // если указан пустой ключ
  551.         return INI_KEY_NOT_FOUND;
  552.     // ----------------
  553.  
  554.  
  555.     // ----------------
  556.     for // перебор и сравнение всех ключей файла с указанным ключом
  557.     (
  558.         new kPos = 0, curFilePos, found = 0;
  559.         kPos < _ini_nKeysCount[nFilePointer];
  560.         kPos++
  561.     )
  562.     {
  563.         // ----------------
  564.         found = 0; // флаг, найдена ли позиция конца ключа = 0
  565.  
  566.         for // ищем позицию конца ключа
  567.         (
  568.             curFilePos = _ini_nDelimPos[nFilePointer][kPos] - 1; // текущ. поз. файла = поз. текущ. INI_DELIMITER - 1
  569.             curFilePos >= 0; // продолжать пока поз. файла >= 0
  570.             curFilePos-- // после каждого повтора текущ. поз. файла -= 1
  571.         )
  572.         {
  573.             switch ( _ini_szFileContent[nFilePointer]{curFilePos} ) // узнаем что за символ в текущ. поз. файла
  574.             {
  575.                 case INI_SPACE_CHAR :           continue; // если это пробельный символ, перейдем к пред. символу файла
  576.                 case INI_STRING_DELIMITER :     break; // если это конец строки
  577.                 default : // если это другой символ
  578.                 {
  579.                     found = 1; // позиция конца ключа найдена
  580.                     break; // конец цикла
  581.                 }
  582.             }
  583.         }
  584.  
  585.         // если позиция конца ключа не найдена, переход к след. позиции INI_DELIMITER
  586.         if ( found != 1 ) continue;
  587.         // ----------------
  588.  
  589.  
  590.         // сравниваем посимвольно текущий ключ файла и указанный ключ
  591.         for ( new curKeyPos = nKeyLen - 1;  curKeyPos >= 0;  curFilePos--, curKeyPos-- )
  592.         {
  593.             if
  594.             (
  595.                     curFilePos < 0 // если поз файла стала < 0
  596.                 ||  _ini_szFileContent[nFilePointer]{curFilePos} != szKeyName[curKeyPos] // если символы из ключей не равны
  597.                 ||  _ini_szFileContent[nFilePointer]{curFilePos} == INI_STRING_DELIMITER // если символ из ключа это INI_STRING_DELIMITER
  598.             )
  599.             {
  600.                 found = 0; // флаг, ключ не найден
  601.                 break; // конец сравнения
  602.             }
  603.         }
  604.  
  605.  
  606.         // ----------------
  607.         if ( found != 0 ) // если указанный ключ найден в файле
  608.         {
  609.             // если найдено совпадение не целого ключа файла, а его окончания с указанным ключом
  610.             if ( curFilePos >= 0 )
  611.             {
  612.                 switch ( _ini_szFileContent[nFilePointer]{curFilePos} )
  613.                 {
  614.                     case INI_KEY_STARTS : {}
  615.                     default: continue;
  616.                 }
  617.             }
  618.  
  619.  
  620.             // ----------------
  621.             // текущая позиция в файле будет на 1 больше текущей позиции INI_DELIMITER
  622.             curFilePos = _ini_nDelimPos[nFilePointer][kPos] + 1;
  623.  
  624.             // ищем позицию начала значения, она будет помещена в curFilePos
  625.             for ( ; ; curFilePos++ )
  626.             {
  627.                 if ( curFilePos >= _ini_nFileSize[nFilePointer] ) break;
  628.  
  629.                 switch ( _ini_szFileContent[nFilePointer]{curFilePos} )
  630.                 {
  631.                     case INI_SPACE_CHAR :   continue; // если это пробельный символ, перейдем к след. символу
  632.                     default :               break; // если это другой символ
  633.                 }
  634.             }
  635.             // ----------------
  636.  
  637.  
  638.             // ----------------
  639.             // скопируем посимвольно в szReturnValue значение ключа из файла
  640.             // воспользуемся созданной переменной found как позицией в возвращаемом значении
  641.             for ( found = 0;  found < nSizeOfReturn;  found++, curFilePos++ )
  642.             {
  643.                 switch ( _ini_szFileContent[nFilePointer]{curFilePos} )
  644.                 {
  645.                     case INI_STRING_ENDS : // если это символ конца значения
  646.                     {
  647.                         szReturnValue[found] = 0; // запишем символ конца строки
  648.                         break; // конец копирования
  649.                     }
  650.                     default :
  651.                         // копируем символ из файла в szReturnValue
  652.                         szReturnValue[found] = _ini_szFileContent[nFilePointer]{curFilePos};
  653.                 }
  654.             }
  655.  
  656.             szReturnValue[nSizeOfReturn - 1] = 0; // на всякий случай обрежем правильно строку
  657.  
  658.             return INI_OK;
  659.             // ----------------
  660.         }
  661.         // ----------------
  662.     }
  663.     // ----------------
  664.  
  665.  
  666.     // ----------------
  667.     return INI_KEY_NOT_FOUND;
  668.     // ----------------
  669. }
  670.  
  671.  
  672.  
  673.  
  674. /*
  675.     Получает из открытого INI файла целочисленное значение указанного ключа.
  676.  
  677.     ПОДРОБНЕЕ
  678.         Парсер ищет в ОЗУ в контенте файла указанный ключ и помещает в
  679.         nReturnValue его целочисленное значение. nReturnValue нужно создать заранее.
  680.  
  681.         Имя ключа в файле может быть любой длины, а также может содержать
  682.         любые символы, кроме 2 символов конца строки \r и \n.
  683.  
  684.         Значение может быть только фиксированной длины, которая равна INI_INTEGER_SIZE - 1.
  685.         Парсер не видит пробелы и знаки табуляции вокруг имени ключа/значения,
  686.         он считает их отступами.
  687.  
  688.     ПАРАМЕТРЫ:
  689.         nFilePointer        ИД_открытого_файла, полученный от ini_openFile / ini_createFile
  690.         szKeyName[]         имя ключа
  691.         nReturnValue        сюда будет помещено значение ключа в виде целого числа
  692.  
  693.     ВЕРНЕТ:
  694.         код ошибки < 0 или 0 при успехе
  695. */
  696.  
  697. stock ini_getInteger ( nFilePointer, szKeyName[], & nReturnValue )
  698. {
  699.     // ----------------
  700.     if // если ИД открытого файла указан неверно
  701.     (
  702.             nFilePointer < 0
  703.         ||  nFilePointer >= INI_MAX_OPENED_FILES
  704.         ||  _ini_nSlotUsed[nFilePointer] != 1
  705.     )
  706.         return INI_WRONG_SLOT; // вернуть код: неверный указатель на открытый файл
  707.     // ----------------
  708.  
  709.  
  710.     // ----------------
  711.     new nKeyLen = strlen(szKeyName); // узнаем длину имени указанного ключа
  712.  
  713.     if ( nKeyLen <= 0 ) // если указан пустой ключ
  714.         return INI_KEY_NOT_FOUND;
  715.     // ----------------
  716.  
  717.  
  718.     // ----------------
  719.     for // перебор и сравнение всех ключей файла с указанным ключом
  720.     (
  721.         new kPos = 0, curFilePos, found = 0;
  722.         kPos < _ini_nKeysCount[nFilePointer];
  723.         kPos++
  724.     )
  725.     {
  726.         // ----------------
  727.         found = 0; // флаг, найдена ли позиция конца ключа = 0
  728.  
  729.         for // ищем позицию конца ключа
  730.         (
  731.             curFilePos = _ini_nDelimPos[nFilePointer][kPos] - 1; // текущ. поз. файла = поз. текущ. INI_DELIMITER - 1
  732.             curFilePos >= 0; // продолжать пока поз. файла >= 0
  733.             curFilePos-- // после каждого повтора текущ. поз. файла -= 1
  734.         )
  735.         {
  736.             switch ( _ini_szFileContent[nFilePointer]{curFilePos} ) // узнаем что за символ в текущ. поз. файла
  737.             {
  738.                 case INI_SPACE_CHAR :           continue; // если это пробельный символ, перейдем к пред. символу файла
  739.                 case INI_STRING_DELIMITER :     break; // если это конец строки
  740.                 default : // если это другой символ
  741.                 {
  742.                     found = 1; // позиция конца ключа найдена
  743.                     break; // конец цикла
  744.                 }
  745.             }
  746.         }
  747.  
  748.         // если позиция конца ключа не найдена, переход к след. позиции INI_DELIMITER
  749.         if ( found != 1 ) continue;
  750.         // ----------------
  751.  
  752.  
  753.         // сравниваем посимвольно текущий ключ файла и указанный ключ
  754.         for ( new curKeyPos = nKeyLen - 1;  curKeyPos >= 0;  curFilePos--, curKeyPos-- )
  755.         {
  756.             if
  757.             (
  758.                     curFilePos < 0 // если поз файла стала < 0
  759.                 ||  _ini_szFileContent[nFilePointer]{curFilePos} != szKeyName[curKeyPos] // если символы из ключей не равны
  760.                 ||  _ini_szFileContent[nFilePointer]{curFilePos} == INI_STRING_DELIMITER // если символ из ключа это INI_STRING_DELIMITER
  761.             )
  762.             {
  763.                 found = 0; // флаг, ключ не найден
  764.                 break; // конец сравнения
  765.             }
  766.         }
  767.  
  768.  
  769.         // ----------------
  770.         if ( found != 0 ) // если указанный ключ найден в файле
  771.         {
  772.             // если найдено совпадение не целого ключа файла, а его окончания с указанным ключом
  773.             if ( curFilePos >= 0 )
  774.             {
  775.                 switch ( _ini_szFileContent[nFilePointer]{curFilePos} )
  776.                 {
  777.                     case INI_KEY_STARTS : {}
  778.                     default: continue;
  779.                 }
  780.             }
  781.  
  782.  
  783.             // ----------------
  784.             // текущая позиция в файле будет на 1 больше текущей позиции INI_DELIMITER
  785.             curFilePos = _ini_nDelimPos[nFilePointer][kPos] + 1;
  786.  
  787.             // ищем позицию начала значения, она будет помещена в curFilePos
  788.             for ( ; ; curFilePos++ )
  789.             {
  790.                 if ( curFilePos >= _ini_nFileSize[nFilePointer] ) break;
  791.  
  792.                 switch ( _ini_szFileContent[nFilePointer]{curFilePos} )
  793.                 {
  794.                     case INI_SPACE_CHAR :   continue; // если это пробельный символ, перейдем к след. символу
  795.                     default :               break; // если это другой символ
  796.                 }
  797.             }
  798.             // ----------------
  799.  
  800.  
  801.             // ----------------
  802.             new strValue[INI_INTEGER_SIZE]; // временная строка для численного значения
  803.  
  804.             // скопируем посимвольно в strValue значение ключа из файла
  805.             // воспользуемся созданной переменной found как позицией в возвращаемом значении
  806.             for ( found = 0;  found < INI_INTEGER_SIZE;  found++, curFilePos++ )
  807.             {
  808.                 switch ( _ini_szFileContent[nFilePointer]{curFilePos} )
  809.                 {
  810.                     case INI_NUMBER_ENDS : // если это символ конца численного значения
  811.                     {
  812.                         strValue[found] = 0; // запишем символ конца строки
  813.                         break; // конец копирования
  814.                     }
  815.                     default :
  816.                         // копируем символ из файла в strValue
  817.                         strValue[found] = _ini_szFileContent[nFilePointer]{curFilePos};
  818.                 }
  819.             }
  820.  
  821.             strValue[INI_INTEGER_SIZE - 1] = 0; // на всякий случай обрежем правильно строку
  822.  
  823.             nReturnValue = strval(strValue); // запишем в nReturnValue численное значение ключа
  824.  
  825.             return INI_OK;
  826.             // ----------------
  827.         }
  828.         // ----------------
  829.     }
  830.     // ----------------
  831.  
  832.  
  833.     // ----------------
  834.     return INI_KEY_NOT_FOUND;
  835.     // ----------------
  836. }
  837.  
  838.  
  839.  
  840.  
  841. /*
  842.     Получает из открытого INI файла дробное численное значение указанного ключа.
  843.  
  844.     ПОДРОБНЕЕ
  845.         Парсер ищет в ОЗУ в контенте файла указанный ключ и помещает в
  846.         fReturnValue его дробное значение. fReturnValue нужно создать заранее.
  847.  
  848.         Имя ключа в файле может быть любой длины, а также может содержать
  849.         любые символы, кроме 2 символов конца строки \r и \n.
  850.  
  851.         Значение может быть только фиксированной длины, которая равна INI_FLOAT_SIZE - 1.
  852.         Парсер не видит пробелы и знаки табуляции вокруг имени ключа/значения,
  853.         он считает их отступами.
  854.  
  855.     ПАРАМЕТРЫ:
  856.         nFilePointer        ИД_открытого_файла, полученный от ini_openFile / ini_createFile
  857.         szKeyName[]         имя ключа
  858.         fReturnValue        сюда будет помещено значение ключа в виде дробного числа
  859.  
  860.     ВЕРНЕТ:
  861.         код ошибки < 0 или 0 при успехе
  862. */
  863.  
  864. stock ini_getFloat ( nFilePointer, szKeyName[], & Float: fReturnValue )
  865. {
  866.     // ----------------
  867.     if // если ИД открытого файла указан неверно
  868.     (
  869.             nFilePointer < 0
  870.         ||  nFilePointer >= INI_MAX_OPENED_FILES
  871.         ||  _ini_nSlotUsed[nFilePointer] != 1
  872.     )
  873.         return INI_WRONG_SLOT; // вернуть код: неверный указатель на открытый файл
  874.     // ----------------
  875.  
  876.  
  877.     // ----------------
  878.     new nKeyLen = strlen(szKeyName); // узнаем длину имени указанного ключа
  879.  
  880.     if ( nKeyLen <= 0 ) // если указан пустой ключ
  881.         return INI_KEY_NOT_FOUND;
  882.     // ----------------
  883.  
  884.  
  885.     // ----------------
  886.     for // перебор и сравнение всех ключей файла с указанным ключом
  887.     (
  888.         new kPos = 0, curFilePos, found = 0;
  889.         kPos < _ini_nKeysCount[nFilePointer];
  890.         kPos++
  891.     )
  892.     {
  893.         // ----------------
  894.         found = 0; // флаг, найдена ли позиция конца ключа = 0
  895.  
  896.         for // ищем позицию конца ключа
  897.         (
  898.             curFilePos = _ini_nDelimPos[nFilePointer][kPos] - 1; // текущ. поз. файла = поз. текущ. INI_DELIMITER - 1
  899.             curFilePos >= 0; // продолжать пока поз. файла >= 0
  900.             curFilePos-- // после каждого повтора текущ. поз. файла -= 1
  901.         )
  902.         {
  903.             switch ( _ini_szFileContent[nFilePointer]{curFilePos} ) // узнаем что за символ в текущ. поз. файла
  904.             {
  905.                 case INI_SPACE_CHAR :           continue; // если это пробельный символ, перейдем к пред. символу файла
  906.                 case INI_STRING_DELIMITER :     break; // если это конец строки
  907.                 default : // если это другой символ
  908.                 {
  909.                     found = 1; // позиция конца ключа найдена
  910.                     break; // конец цикла
  911.                 }
  912.             }
  913.         }
  914.  
  915.         // если позиция конца ключа не найдена, переход к след. позиции INI_DELIMITER
  916.         if ( found != 1 ) continue;
  917.         // ----------------
  918.  
  919.  
  920.         // сравниваем посимвольно текущий ключ файла и указанный ключ
  921.         for ( new curKeyPos = nKeyLen - 1;  curKeyPos >= 0;  curFilePos--, curKeyPos-- )
  922.         {
  923.             if
  924.             (
  925.                     curFilePos < 0 // если поз файла стала < 0
  926.                 ||  _ini_szFileContent[nFilePointer]{curFilePos} != szKeyName[curKeyPos] // если символы из ключей не равны
  927.                 ||  _ini_szFileContent[nFilePointer]{curFilePos} == INI_STRING_DELIMITER // если символ из ключа это INI_STRING_DELIMITER
  928.             )
  929.             {
  930.                 found = 0; // флаг, ключ не найден
  931.                 break; // конец сравнения
  932.             }
  933.         }
  934.  
  935.  
  936.         // ----------------
  937.         if ( found != 0 ) // если указанный ключ найден в файле
  938.         {
  939.             // если найдено совпадение не целого ключа файла, а его окончания с указанным ключом
  940.             if ( curFilePos >= 0 )
  941.             {
  942.                 switch ( _ini_szFileContent[nFilePointer]{curFilePos} )
  943.                 {
  944.                     case INI_KEY_STARTS : {}
  945.                     default: continue;
  946.                 }
  947.             }
  948.  
  949.  
  950.             // ----------------
  951.             // текущая позиция в файле будет на 1 больше текущей позиции INI_DELIMITER
  952.             curFilePos = _ini_nDelimPos[nFilePointer][kPos] + 1;
  953.  
  954.             // ищем позицию начала значения, она будет помещена в curFilePos
  955.             for ( ; ; curFilePos++ )
  956.             {
  957.                 if ( curFilePos >= _ini_nFileSize[nFilePointer] ) break;
  958.  
  959.                 switch ( _ini_szFileContent[nFilePointer]{curFilePos} )
  960.                 {
  961.                     case INI_SPACE_CHAR :   continue; // если это пробельный символ, перейдем к след. символу
  962.                     default :               break; // если это другой символ
  963.                 }
  964.             }
  965.             // ----------------
  966.  
  967.  
  968.             // ----------------
  969.             new strValue[INI_FLOAT_SIZE]; // временная строка для дробного значения
  970.  
  971.             // скопируем посимвольно в strValue значение ключа из файла
  972.             // воспользуемся созданной переменной found как позицией в возвращаемом значении
  973.             for ( found = 0;  found < INI_INTEGER_SIZE;  found++, curFilePos++ )
  974.             {
  975.                 switch ( _ini_szFileContent[nFilePointer]{curFilePos} )
  976.                 {
  977.                     case INI_NUMBER_ENDS : // если это символ конца численного значения
  978.                     {
  979.                         strValue[found] = 0; // запишем символ конца строки
  980.                         break; // конец копирования
  981.                     }
  982.                     default :
  983.                         // копируем символ из файла в strValue
  984.                         strValue[found] = _ini_szFileContent[nFilePointer]{curFilePos};
  985.                 }
  986.             }
  987.  
  988.             strValue[INI_FLOAT_SIZE - 1] = 0; // на всякий случай обрежем правильно строку
  989.  
  990.             fReturnValue = floatstr(strValue); // запишем в fReturnValue дробное значение ключа
  991.  
  992.             return INI_OK;
  993.             // ----------------
  994.         }
  995.         // ----------------
  996.     }
  997.     // ----------------
  998.  
  999.  
  1000.     // ----------------
  1001.     return INI_KEY_NOT_FOUND;
  1002.     // ----------------
  1003. }
  1004.  
  1005.  
  1006.  
  1007.  
  1008.  
  1009.  
  1010.  
  1011.  
  1012.  
  1013.  
  1014.  
  1015. /*
  1016.     Изменяет/добавляет в открытый INI файл указанный ключ и его значение.
  1017.  
  1018.     ПОДРОБНЕЕ
  1019.         Парсер ищет в ОЗУ в контенте файла указанный ключ и изменяет его
  1020.         строковое значение на szKeyValue.
  1021.  
  1022.         Форматирование файла не меняется. Если только указанное имя ключа
  1023.         не было найдено, тогда ключ и значение будут добавлены в конец файла.
  1024.  
  1025.         Имя ключа/значение в файле может быть любой длины, а также может содержать
  1026.         любые символы, кроме 2 символов конца строки \r и \n. Если эти символы есть
  1027.         в имени ключа или в значении, вы должны должны хорошо знать и осознавать
  1028.         последствия.
  1029.  
  1030.     ПАРАМЕТРЫ:
  1031.         nFilePointer        ИД_открытого_файла, полученный от ini_openFile / ini_createFile
  1032.         szKeyName[]         имя ключа
  1033.         szKeyValue[]        строка со значением ключа
  1034.  
  1035.     ВЕРНЕТ:
  1036.         код ошибки < 0 или 0 при успехе
  1037. */
  1038.  
  1039. stock ini_setString ( nFilePointer, szKeyName[], szKeyValue[] )
  1040. {
  1041.     // ----------------
  1042.     if // если ИД открытого файла указан неверно
  1043.     (
  1044.             nFilePointer < 0
  1045.         ||  nFilePointer >= INI_MAX_OPENED_FILES
  1046.         ||  _ini_nSlotUsed[nFilePointer] != 1
  1047.     )
  1048.         return INI_WRONG_SLOT; // вернуть код: неверный указатель на открытый файл
  1049.     // ----------------
  1050.  
  1051.  
  1052.     // ----------------
  1053.     new nKeyLen = strlen(szKeyName); // узнаем длину имени указанного ключа
  1054.  
  1055.     if ( nKeyLen <= 0 ) // если указан пустой ключ
  1056.         return INI_KEY_NOT_FOUND;
  1057.     // ----------------
  1058.  
  1059.  
  1060.     // ----------------
  1061.     for // перебор и сравнение всех ключей файла с указанным ключом
  1062.     (
  1063.         new kPos = 0, curFilePos, found;
  1064.         kPos < _ini_nKeysCount[nFilePointer];
  1065.         kPos++
  1066.     )
  1067.     {
  1068.         // ----------------
  1069.         found = 0; // флаг, найдена ли позиция конца ключа = 0
  1070.  
  1071.         for // ищем позицию конца ключа
  1072.         (
  1073.             curFilePos = _ini_nDelimPos[nFilePointer][kPos] - 1; // текущ. поз. файла = поз. текущ. INI_DELIMITER - 1
  1074.             curFilePos >= 0; // продолжать пока поз. файла >= 0
  1075.             curFilePos-- // после каждого повтора текущ. поз. файла -= 1
  1076.         )
  1077.         {
  1078.             switch ( _ini_szFileContent[nFilePointer]{curFilePos} ) // узнаем что за символ в текущ. поз. файла
  1079.             {
  1080.                 case INI_SPACE_CHAR :           continue; // если это пробельный символ, перейдем к пред. символу файла
  1081.                 case INI_STRING_DELIMITER :     break; // если это конец строки
  1082.                 default : // если это другой символ
  1083.                 {
  1084.                     found = 1; // позиция конца ключа найдена
  1085.                     break; // конец цикла
  1086.                 }
  1087.             }
  1088.         }
  1089.  
  1090.         // если позиция конца ключа не найдена, переход к след. позиции INI_DELIMITER
  1091.         if ( found != 1 ) continue;
  1092.         // ----------------
  1093.  
  1094.  
  1095.         // сравниваем посимвольно текущий ключ файла и указанный ключ
  1096.         for ( new curKeyPos = nKeyLen - 1;  curKeyPos >= 0;  curFilePos--, curKeyPos-- )
  1097.         {
  1098.             if
  1099.             (
  1100.                     curFilePos < 0 // если поз файла стала < 0
  1101.                 ||  _ini_szFileContent[nFilePointer]{curFilePos} != szKeyName[curKeyPos] // если символы из ключей не равны
  1102.                 ||  _ini_szFileContent[nFilePointer]{curFilePos} == INI_STRING_DELIMITER // если символ из ключа это INI_STRING_DELIMITER
  1103.             )
  1104.             {
  1105.                 found = 0; // флаг, ключ не найден
  1106.                 break; // конец сравнения
  1107.             }
  1108.         }
  1109.  
  1110.  
  1111.         if ( found != 0 ) // если указанный ключ НАЙДЕН в файле
  1112.         {
  1113.             // если найдено совпадение не целого ключа файла, а его окончания с указанным ключом
  1114.             if ( curFilePos >= 0 )
  1115.             {
  1116.                 switch ( _ini_szFileContent[nFilePointer]{curFilePos} )
  1117.                 {
  1118.                     case INI_KEY_STARTS : {}
  1119.                     default: continue;
  1120.                 }
  1121.             }
  1122.  
  1123.  
  1124.             // ----------------
  1125.             // текущая позиция в файле будет на 1 больше текущей позиции INI_DELIMITER
  1126.             curFilePos = _ini_nDelimPos[nFilePointer][kPos] + 1;
  1127.  
  1128.             // ищем позицию начала значения, она будет помещена в curFilePos
  1129.             for ( ; ; curFilePos++ )
  1130.             {
  1131.                 if ( curFilePos >= _ini_nFileSize[nFilePointer] ) break;
  1132.  
  1133.                 switch ( _ini_szFileContent[nFilePointer]{curFilePos} )
  1134.                 {
  1135.                     case INI_SPACE_CHAR :   continue; // если это пробельный символ, перейдем к след. символу
  1136.                     default :               break; // если это другой символ
  1137.                 }
  1138.             }
  1139.             // ----------------
  1140.  
  1141.  
  1142.             // ----------------
  1143.             new nValueLen = strlen(szKeyValue); // узнаем размер значения указанного ключа
  1144.  
  1145.             // если будущий размер файла превышает лимит
  1146.             if ( ( curFilePos + nValueLen ) >= INI_MAX_FILE_SIZE )
  1147.                 return INI_TOO_LARGE_FILE; // вернем код ошибки о переполнении файла
  1148.             // ----------------
  1149.  
  1150.  
  1151.             // ----------------
  1152.             new fileValueStartPos = curFilePos; // сохраним позицию начала значения
  1153.  
  1154.             // ищем позицию конца значения, она будет помещена в curFilePos
  1155.             for ( ; ; curFilePos++ )
  1156.             {
  1157.                 if ( curFilePos >= _ini_nFileSize[nFilePointer] ) break;
  1158.  
  1159.                 switch ( _ini_szFileContent[nFilePointer]{curFilePos} )
  1160.                 {
  1161.                     case INI_STRING_ENDS :  break; // если это конец строки - стоп
  1162.                     default :               continue; // если это другой символ, перейдем к след. символу
  1163.                 }
  1164.             }
  1165.  
  1166.             // вычислим смещение оставшихся позиций для INI_DELIMITER после изменения значения
  1167.             new filePosOffset = nValueLen - ( /*текущ длина значения*/ curFilePos - fileValueStartPos );
  1168.             // ----------------
  1169.  
  1170.  
  1171.             if ( _ini_nFileBusy[nFilePointer] != 0 ) // если прямо сейчас файл изменяется другой функцией
  1172.                 return INI_WRITE_ERROR; // вернем код ошибки при записи в файл
  1173.  
  1174.  
  1175.             // ----------------
  1176.             _ini_nFileBusy[nFilePointer] = 1; // флаг: файл изменяется = 1
  1177.  
  1178.             if ( filePosOffset != 0 ) // если длины старого и нового значений разные
  1179.             {
  1180.                 if ( filePosOffset < 0 ) // если длина нового значения меньше
  1181.                 {
  1182.                     for // копируем символы, стоящие после текущего значения на их новые места,
  1183.                     ( // которые теперь будут ближе к началу файла
  1184.                         ;
  1185.                         curFilePos < _ini_nFileSize[nFilePointer];
  1186.                         curFilePos++
  1187.                     )
  1188.                         _ini_szFileContent[nFilePointer]{ curFilePos + filePosOffset } =
  1189.                             _ini_szFileContent[nFilePointer]{curFilePos};
  1190.                 }
  1191.                 else if ( filePosOffset > 0 ) // если длина нового значения больше
  1192.                 {
  1193.                     new fileValueEndPos = curFilePos;
  1194.  
  1195.                     for // копируем символы, стоящие после текущего значения на их новые места,
  1196.                     ( // начиная с конца файла
  1197.                         curFilePos = _ini_nFileSize[nFilePointer] - 1;
  1198.                         curFilePos >= fileValueEndPos;
  1199.                         curFilePos--
  1200.                     )
  1201.                         _ini_szFileContent[nFilePointer]{ curFilePos + filePosOffset } =
  1202.                             _ini_szFileContent[nFilePointer]{curFilePos};
  1203.                 }
  1204.  
  1205.                 // изменим позиции всех INI_DELIMITER, которые находились после текущего INI_DELIMITER
  1206.                 for ( kPos++;  kPos < _ini_nKeysCount[nFilePointer];  kPos++ )
  1207.                     _ini_nDelimPos[nFilePointer][kPos] += filePosOffset;
  1208.  
  1209.                 _ini_nFileSize[nFilePointer] += filePosOffset; // изменим размер файла
  1210.                 _ini_szFileContent[nFilePointer]{ _ini_nFileSize[nFilePointer] } = 0; // конец строки для конца файла
  1211.             }
  1212.  
  1213.             curFilePos = fileValueStartPos; // текущ поз файла = поз начала старого значения
  1214.             for // запишем новое значение поверх старого
  1215.             (
  1216.                 new valuePos = 0;
  1217.                 valuePos < nValueLen;
  1218.                 curFilePos++, valuePos++
  1219.             )
  1220.                 _ini_szFileContent[nFilePointer]{curFilePos} = szKeyValue[valuePos];
  1221.  
  1222.             _ini_nFileChanged[nFilePointer]     = 1; // флаг: файл изменен = 1
  1223.             _ini_nFileBusy[nFilePointer]        = 0; // флаг: файл изменяется = 0
  1224.  
  1225.             return INI_OK;
  1226.             // ----------------
  1227.         }
  1228.     }
  1229.     // ----------------
  1230.  
  1231.  
  1232.     //
  1233.     // если указанный ключ НЕ НАЙДЕН в файле
  1234.     //
  1235.  
  1236.     // ----------------
  1237.     new nValueLen = strlen(szKeyValue); // узнаем размер значения ключа
  1238.  
  1239.     // если будущий размер файла превышает лимит
  1240.     if ( ( _ini_nFileSize[nFilePointer] + 5 + nKeyLen + nValueLen ) >= INI_MAX_FILE_SIZE )
  1241.         return INI_TOO_LARGE_FILE; // вернем код ошибки о переполнении файла
  1242.     // ----------------
  1243.  
  1244.  
  1245.     // ----------------
  1246.     if ( _ini_nFileBusy[nFilePointer] != 0 ) // если прямо сейчас файл изменяется другой функцией
  1247.         return INI_WRITE_ERROR; // вернем код ошибки при записи в файл
  1248.  
  1249.     _ini_nFileBusy[nFilePointer] = 1; // флаг: файл изменяется = 1
  1250.     // ----------------
  1251.  
  1252.  
  1253.     // ----------------
  1254.     if // если последний символ файла не является разделителем строк
  1255.     (
  1256.             _ini_nFileSize[nFilePointer] > 0
  1257.         &&  _ini_szFileContent[nFilePointer]{ _ini_nFileSize[nFilePointer] - 1 } != INI_STRING_DELIMITER
  1258.     )
  1259.     {
  1260.         // вставим в конец файла перевод каретки и разделитель строк
  1261.         _ini_szFileContent[nFilePointer]{ _ini_nFileSize[nFilePointer] }        = '\r';
  1262.         _ini_szFileContent[nFilePointer]{ _ini_nFileSize[nFilePointer] + 1 }    = INI_STRING_DELIMITER;
  1263.  
  1264.         _ini_nFileSize[nFilePointer] += 2; // увеличим размер файла на 2
  1265.     }
  1266.  
  1267.     new curFilePos, strPos; // будут временным хранилищами позиций в файле/ключе/значении
  1268.  
  1269.     for // добавим посимвольно имя ключа в файл
  1270.     (
  1271.         curFilePos = _ini_nFileSize[nFilePointer], strPos = 0;
  1272.         strPos < nKeyLen;
  1273.         curFilePos++, strPos++
  1274.     )
  1275.         _ini_szFileContent[nFilePointer]{curFilePos} = szKeyName[strPos];
  1276.  
  1277.     _ini_szFileContent[nFilePointer]{ curFilePos++ }                = ' '; // после ключа добавим пробел
  1278.  
  1279.     _ini_nDelimPos[nFilePointer][ _ini_nKeysCount[nFilePointer] ]   = curFilePos; // добавим новую позицию INI_DELIMITER
  1280.     _ini_nKeysCount[nFilePointer]++; // кол-во ключей в файле +1
  1281.  
  1282.     _ini_szFileContent[nFilePointer]{ curFilePos++ }                = INI_DELIMITER; // после пробела выше добавим INI_DELIMITER
  1283.     _ini_szFileContent[nFilePointer]{ curFilePos++ }                = ' '; // после INI_DELIMITER добавим пробел
  1284.  
  1285.     // добавим посимвольно значение ключа в файл
  1286.     for ( strPos = 0;  strPos < nValueLen;  curFilePos++, strPos++ )
  1287.         _ini_szFileContent[nFilePointer]{curFilePos} = szKeyValue[strPos];
  1288.  
  1289.     _ini_szFileContent[nFilePointer]{curFilePos}    = 0; // добавим символ конца строки
  1290.     _ini_nFileSize[nFilePointer]                    = curFilePos; // изменим размер файла
  1291.     _ini_nFileChanged[nFilePointer]                 = 1; // флаг: файл изменен = 1
  1292.     _ini_nFileBusy[nFilePointer]                    = 0; // флаг: файл изменяется = 0
  1293.  
  1294.     return INI_OK;
  1295.     // ----------------
  1296. }
  1297.  
  1298.  
  1299.  
  1300.  
  1301. /*
  1302.     Изменяет/добавляет в открытый INI файл указанный ключ и его целочисленное значение.
  1303.  
  1304.     ПОДРОБНЕЕ
  1305.         Парсер ищет в ОЗУ в контенте файла указанный ключ и изменяет его
  1306.         целочисленное значение на nKeyValue.
  1307.  
  1308.         Форматирование файла не меняется, если только указанное имя ключа
  1309.         не было найдено, тогда ключ и значение будут добавлены в конец файла.
  1310.  
  1311.         Имя ключа в файле может быть любой длины, а также может содержать
  1312.         любые символы, кроме 2 символов конца строки \r и \n. Если вы в качестве
  1313.         значения указываете число больше, чем cellmax или меньше, чем cellmin,
  1314.         вы должны должны хорошо знать и осознавать последствия.
  1315.  
  1316.     ПАРАМЕТРЫ:
  1317.         nFilePointer        ИД_открытого_файла, полученный от ini_openFile / ini_createFile
  1318.         szKeyName[]         имя ключа
  1319.         nKeyValue           целочисленное значение ключа
  1320.  
  1321.     ВЕРНЕТ:
  1322.         код ошибки < 0 или 0 при успехе
  1323. */
  1324.  
  1325. stock ini_setInteger ( nFilePointer, szKeyName[], nKeyValue )
  1326. {
  1327.     // ----------------
  1328.     if // если ИД открытого файла указан неверно
  1329.     (
  1330.             nFilePointer < 0
  1331.         ||  nFilePointer >= INI_MAX_OPENED_FILES
  1332.         ||  _ini_nSlotUsed[nFilePointer] != 1
  1333.     )
  1334.         return INI_WRONG_SLOT; // вернуть код: неверный указатель на открытый файл
  1335.     // ----------------
  1336.  
  1337.  
  1338.     // ----------------
  1339.     new nKeyLen = strlen(szKeyName); // узнаем длину имени указанного ключа
  1340.  
  1341.     if ( nKeyLen <= 0 ) // если указан пустой ключ
  1342.         return INI_KEY_NOT_FOUND;
  1343.     // ----------------
  1344.  
  1345.  
  1346.     // ----------------
  1347.     for // перебор и сравнение всех ключей файла с указанным ключом
  1348.     (
  1349.         new kPos = 0, curFilePos, found;
  1350.         kPos < _ini_nKeysCount[nFilePointer];
  1351.         kPos++
  1352.     )
  1353.     {
  1354.         // ----------------
  1355.         found = 0; // флаг, найдена ли позиция конца ключа = 0
  1356.  
  1357.         for // ищем позицию конца ключа
  1358.         (
  1359.             curFilePos = _ini_nDelimPos[nFilePointer][kPos] - 1; // текущ. поз. файла = поз. текущ. INI_DELIMITER - 1
  1360.             curFilePos >= 0; // продолжать пока поз. файла >= 0
  1361.             curFilePos-- // после каждого повтора текущ. поз. файла -= 1
  1362.         )
  1363.         {
  1364.             switch ( _ini_szFileContent[nFilePointer]{curFilePos} ) // узнаем что за символ в текущ. поз. файла
  1365.             {
  1366.                 case INI_SPACE_CHAR :           continue; // если это пробельный символ, перейдем к пред. символу файла
  1367.                 case INI_STRING_DELIMITER :     break; // если это конец строки
  1368.                 default : // если это другой символ
  1369.                 {
  1370.                     found = 1; // позиция конца ключа найдена
  1371.                     break; // конец цикла
  1372.                 }
  1373.             }
  1374.         }
  1375.  
  1376.         // если позиция конца ключа не найдена, переход к след. позиции INI_DELIMITER
  1377.         if ( found != 1 ) continue;
  1378.         // ----------------
  1379.  
  1380.  
  1381.         // сравниваем посимвольно текущий ключ файла и указанный ключ
  1382.         for ( new curKeyPos = nKeyLen - 1;  curKeyPos >= 0;  curFilePos--, curKeyPos-- )
  1383.         {
  1384.             if
  1385.             (
  1386.                     curFilePos < 0 // если поз файла стала < 0
  1387.                 ||  _ini_szFileContent[nFilePointer]{curFilePos} != szKeyName[curKeyPos] // если символы из ключей не равны
  1388.                 ||  _ini_szFileContent[nFilePointer]{curFilePos} == INI_STRING_DELIMITER // если символ из ключа это INI_STRING_DELIMITER
  1389.             )
  1390.             {
  1391.                 found = 0; // флаг, ключ не найден
  1392.                 break; // конец сравнения
  1393.             }
  1394.         }
  1395.  
  1396.  
  1397.         if ( found != 0 ) // если указанный ключ НАЙДЕН в файле
  1398.         {
  1399.             // если найдено совпадение не целого ключа файла, а его окончания с указанным ключом
  1400.             if ( curFilePos >= 0 )
  1401.             {
  1402.                 switch ( _ini_szFileContent[nFilePointer]{curFilePos} )
  1403.                 {
  1404.                     case INI_KEY_STARTS : {}
  1405.                     default: continue;
  1406.                 }
  1407.             }
  1408.  
  1409.  
  1410.             // ----------------
  1411.             // текущая позиция в файле будет на 1 больше текущей позиции INI_DELIMITER
  1412.             curFilePos = _ini_nDelimPos[nFilePointer][kPos] + 1;
  1413.  
  1414.             // ищем позицию начала значения, она будет помещена в curFilePos
  1415.             for ( ; ; curFilePos++ )
  1416.             {
  1417.                 if ( curFilePos >= _ini_nFileSize[nFilePointer] ) break;
  1418.  
  1419.                 switch ( _ini_szFileContent[nFilePointer]{curFilePos} )
  1420.                 {
  1421.                     case INI_SPACE_CHAR :   continue; // если это пробельный символ, перейдем к след. символу
  1422.                     default :               break; // если это другой символ
  1423.                 }
  1424.             }
  1425.             // ----------------
  1426.  
  1427.  
  1428.             // ----------------
  1429.             new szKeyValue[INI_INTEGER_SIZE]; // создадим строку для хранения целочисленного значения
  1430.             format( szKeyValue, INI_INTEGER_SIZE, "%i", nKeyValue );
  1431.             // ----------------
  1432.  
  1433.  
  1434.             // ----------------
  1435.             new nValueLen = strlen(szKeyValue); // узнаем размер значения указанного ключа
  1436.  
  1437.             // если будущий размер файла превышает лимит
  1438.             if ( ( curFilePos + nValueLen ) >= INI_MAX_FILE_SIZE )
  1439.                 return INI_TOO_LARGE_FILE; // вернем код ошибки о переполнении файла
  1440.             // ----------------
  1441.  
  1442.  
  1443.             // ----------------
  1444.             new fileValueStartPos = curFilePos; // сохраним позицию начала значения
  1445.  
  1446.             // ищем позицию конца значения, она будет помещена в curFilePos
  1447.             for ( ; ; curFilePos++ )
  1448.             {
  1449.                 if ( curFilePos >= _ini_nFileSize[nFilePointer] ) break;
  1450.  
  1451.                 switch ( _ini_szFileContent[nFilePointer]{curFilePos} )
  1452.                 {
  1453.                     case INI_NUMBER_ENDS :  break; // если это конец числа - стоп
  1454.                     default :               continue; // если это другой символ, перейдем к след. символу
  1455.                 }
  1456.             }
  1457.  
  1458.             // вычислим смещение оставшихся позиций для INI_DELIMITER после изменения значения
  1459.             new filePosOffset = nValueLen - ( /*текущ длина значения*/ curFilePos - fileValueStartPos );
  1460.             // ----------------
  1461.  
  1462.  
  1463.             if ( _ini_nFileBusy[nFilePointer] != 0 ) // если прямо сейчас файл изменяется другой функцией
  1464.                 return INI_WRITE_ERROR; // вернем код ошибки при записи в файл
  1465.  
  1466.  
  1467.             // ----------------
  1468.             _ini_nFileBusy[nFilePointer] = 1; // флаг: файл изменяется = 1
  1469.  
  1470.             if ( filePosOffset != 0 ) // если длины старого и нового значений разные
  1471.             {
  1472.                 if ( filePosOffset < 0 ) // если длина нового значения меньше
  1473.                 {
  1474.                     for // копируем символы, стоящие после текущего значения на их новые места,
  1475.                     ( // которые теперь будут ближе к началу файла
  1476.                         ;
  1477.                         curFilePos < _ini_nFileSize[nFilePointer];
  1478.                         curFilePos++
  1479.                     )
  1480.                         _ini_szFileContent[nFilePointer]{ curFilePos + filePosOffset } =
  1481.                             _ini_szFileContent[nFilePointer]{curFilePos};
  1482.                 }
  1483.                 else if ( filePosOffset > 0 ) // если длина нового значения больше
  1484.                 {
  1485.                     new fileValueEndPos = curFilePos;
  1486.  
  1487.                     for // копируем символы, стоящие после текущего значения на их новые места,
  1488.                     ( // начиная с конца файла
  1489.                         curFilePos = _ini_nFileSize[nFilePointer] - 1;
  1490.                         curFilePos >= fileValueEndPos;
  1491.                         curFilePos--
  1492.                     )
  1493.                         _ini_szFileContent[nFilePointer]{ curFilePos + filePosOffset } =
  1494.                             _ini_szFileContent[nFilePointer]{curFilePos};
  1495.                 }
  1496.  
  1497.                 // изменим позиции всех INI_DELIMITER, которые находились после текущего INI_DELIMITER
  1498.                 for ( kPos++;  kPos < _ini_nKeysCount[nFilePointer];  kPos++ )
  1499.                     _ini_nDelimPos[nFilePointer][kPos] += filePosOffset;
  1500.  
  1501.                 _ini_nFileSize[nFilePointer] += filePosOffset; // изменим размер файла
  1502.                 _ini_szFileContent[nFilePointer]{ _ini_nFileSize[nFilePointer] } = 0; // конец строки для конца файла
  1503.             }
  1504.  
  1505.             curFilePos = fileValueStartPos; // текущ поз файла = поз начала старого значения
  1506.             for // запишем новое значение поверх старого
  1507.             (
  1508.                 new valuePos = 0;
  1509.                 valuePos < nValueLen;
  1510.                 curFilePos++, valuePos++
  1511.             )
  1512.                 _ini_szFileContent[nFilePointer]{curFilePos} = szKeyValue[valuePos];
  1513.  
  1514.             _ini_nFileChanged[nFilePointer]     = 1; // флаг: файл изменен = 1
  1515.             _ini_nFileBusy[nFilePointer]        = 0; // флаг: файл изменяется = 0
  1516.  
  1517.             return INI_OK;
  1518.             // ----------------
  1519.         }
  1520.     }
  1521.     // ----------------
  1522.  
  1523.  
  1524.     //
  1525.     // если указанный ключ НЕ НАЙДЕН в файле
  1526.     //
  1527.  
  1528.     // ----------------
  1529.     new szKeyValue[INI_INTEGER_SIZE]; // создадим строку для хранения целочисленного значения
  1530.     format( szKeyValue, INI_INTEGER_SIZE, "%i", nKeyValue );
  1531.     // ----------------
  1532.  
  1533.  
  1534.     // ----------------
  1535.     new nValueLen = strlen(szKeyValue); // узнаем размер значения ключа
  1536.  
  1537.     // если будущий размер файла превышает лимит
  1538.     if ( ( _ini_nFileSize[nFilePointer] + 5 + nKeyLen + nValueLen ) >= INI_MAX_FILE_SIZE )
  1539.         return INI_TOO_LARGE_FILE; // вернем код ошибки о переполнении файла
  1540.     // ----------------
  1541.  
  1542.  
  1543.     // ----------------
  1544.     if ( _ini_nFileBusy[nFilePointer] != 0 ) // если прямо сейчас файл изменяется другой функцией
  1545.         return INI_WRITE_ERROR; // вернем код ошибки при записи в файл
  1546.  
  1547.     _ini_nFileBusy[nFilePointer] = 1; // флаг: файл изменяется = 1
  1548.     // ----------------
  1549.  
  1550.  
  1551.     // ----------------
  1552.     if // если последний символ файла не является разделителем строк
  1553.     (
  1554.             _ini_nFileSize[nFilePointer] > 0
  1555.         &&  _ini_szFileContent[nFilePointer]{ _ini_nFileSize[nFilePointer] - 1 } != INI_STRING_DELIMITER
  1556.     )
  1557.     {
  1558.         // вставим в конец файла перевод каретки и разделитель строк
  1559.         _ini_szFileContent[nFilePointer]{ _ini_nFileSize[nFilePointer] }        = '\r';
  1560.         _ini_szFileContent[nFilePointer]{ _ini_nFileSize[nFilePointer] + 1 }    = INI_STRING_DELIMITER;
  1561.  
  1562.         _ini_nFileSize[nFilePointer] += 2; // увеличим размер файла на 2
  1563.     }
  1564.  
  1565.     new curFilePos, strPos; // будут временным хранилищами позиций в файле/ключе/значении
  1566.  
  1567.     for // добавим посимвольно имя ключа в файл
  1568.     (
  1569.         curFilePos = _ini_nFileSize[nFilePointer], strPos = 0;
  1570.         strPos < nKeyLen;
  1571.         curFilePos++, strPos++
  1572.     )
  1573.         _ini_szFileContent[nFilePointer]{curFilePos} = szKeyName[strPos];
  1574.  
  1575.     _ini_szFileContent[nFilePointer]{ curFilePos++ }                = ' '; // после ключа добавим пробел
  1576.  
  1577.     _ini_nDelimPos[nFilePointer][ _ini_nKeysCount[nFilePointer] ]   = curFilePos; // добавим новую позицию INI_DELIMITER
  1578.     _ini_nKeysCount[nFilePointer]++; // кол-во ключей в файле +1
  1579.  
  1580.     _ini_szFileContent[nFilePointer]{ curFilePos++ }                = INI_DELIMITER; // после пробела выше добавим INI_DELIMITER
  1581.     _ini_szFileContent[nFilePointer]{ curFilePos++ }                = ' '; // после INI_DELIMITER добавим пробел
  1582.  
  1583.     // добавим посимвольно значение ключа в файл
  1584.     for ( strPos = 0;  strPos < nValueLen;  curFilePos++, strPos++ )
  1585.         _ini_szFileContent[nFilePointer]{curFilePos} = szKeyValue[strPos];
  1586.  
  1587.     _ini_szFileContent[nFilePointer]{curFilePos}    = 0; // добавим символ конца строки
  1588.     _ini_nFileSize[nFilePointer]                    = curFilePos; // изменим размер файла
  1589.     _ini_nFileChanged[nFilePointer]                 = 1; // флаг: файл изменен = 1
  1590.     _ini_nFileBusy[nFilePointer]                    = 0; // флаг: файл изменяется = 0
  1591.  
  1592.     return INI_OK;
  1593.     // ----------------
  1594. }
  1595.  
  1596.  
  1597.  
  1598.  
  1599. /*
  1600.     Изменяет/добавляет в открытый INI файл указанный ключ и его дробное численное значение.
  1601.  
  1602.     ПОДРОБНЕЕ
  1603.         Парсер ищет в ОЗУ в контенте файла указанный ключ и изменяет его
  1604.         дробное значение на fKeyValue.
  1605.  
  1606.         Форматирование файла не меняется, если только указанное имя ключа
  1607.         не было найдено, тогда ключ и значение будут добавлены в конец файла.
  1608.  
  1609.         Имя ключа в файле может быть любой длины, а также может содержать
  1610.         любые символы, кроме 2 символов конца строки \r и \n. Если вы в качестве
  1611.         значения указываете очень большое/маленькое дробное число, вы должны
  1612.         хорошо знать и осознавать последствия.
  1613.  
  1614.     ПАРАМЕТРЫ:
  1615.         nFilePointer        ИД_открытого_файла, полученный от ini_openFile / ini_createFile
  1616.         szKeyName[]         имя ключа
  1617.         fKeyValue           дробное численное значение ключа
  1618.  
  1619.     ВЕРНЕТ:
  1620.         код ошибки < 0 или 0 при успехе
  1621. */
  1622.  
  1623. stock ini_setFloat ( nFilePointer, szKeyName[], Float: fKeyValue )
  1624. {
  1625.     // ----------------
  1626.     if // если ИД открытого файла указан неверно
  1627.     (
  1628.             nFilePointer < 0
  1629.         ||  nFilePointer >= INI_MAX_OPENED_FILES
  1630.         ||  _ini_nSlotUsed[nFilePointer] != 1
  1631.     )
  1632.         return INI_WRONG_SLOT; // вернуть код: неверный указатель на открытый файл
  1633.     // ----------------
  1634.  
  1635.  
  1636.     // ----------------
  1637.     new nKeyLen = strlen(szKeyName); // узнаем длину имени указанного ключа
  1638.  
  1639.     if ( nKeyLen <= 0 ) // если указан пустой ключ
  1640.         return INI_KEY_NOT_FOUND;
  1641.     // ----------------
  1642.  
  1643.  
  1644.     // ----------------
  1645.     for // перебор и сравнение всех ключей файла с указанным ключом
  1646.     (
  1647.         new kPos = 0, curFilePos, found;
  1648.         kPos < _ini_nKeysCount[nFilePointer];
  1649.         kPos++
  1650.     )
  1651.     {
  1652.         // ----------------
  1653.         found = 0; // флаг, найдена ли позиция конца ключа = 0
  1654.  
  1655.         for // ищем позицию конца ключа
  1656.         (
  1657.             curFilePos = _ini_nDelimPos[nFilePointer][kPos] - 1; // текущ. поз. файла = поз. текущ. INI_DELIMITER - 1
  1658.             curFilePos >= 0; // продолжать пока поз. файла >= 0
  1659.             curFilePos-- // после каждого повтора текущ. поз. файла -= 1
  1660.         )
  1661.         {
  1662.             switch ( _ini_szFileContent[nFilePointer]{curFilePos} ) // узнаем что за символ в текущ. поз. файла
  1663.             {
  1664.                 case INI_SPACE_CHAR :           continue; // если это пробельный символ, перейдем к пред. символу файла
  1665.                 case INI_STRING_DELIMITER :     break; // если это конец строки
  1666.                 default : // если это другой символ
  1667.                 {
  1668.                     found = 1; // позиция конца ключа найдена
  1669.                     break; // конец цикла
  1670.                 }
  1671.             }
  1672.         }
  1673.  
  1674.         // если позиция конца ключа не найдена, переход к след. позиции INI_DELIMITER
  1675.         if ( found != 1 ) continue;
  1676.         // ----------------
  1677.  
  1678.  
  1679.         // сравниваем посимвольно текущий ключ файла и указанный ключ
  1680.         for ( new curKeyPos = nKeyLen - 1;  curKeyPos >= 0;  curFilePos--, curKeyPos-- )
  1681.         {
  1682.             if
  1683.             (
  1684.                     curFilePos < 0 // если поз файла стала < 0
  1685.                 ||  _ini_szFileContent[nFilePointer]{curFilePos} != szKeyName[curKeyPos] // если символы из ключей не равны
  1686.                 ||  _ini_szFileContent[nFilePointer]{curFilePos} == INI_STRING_DELIMITER // если символ из ключа это INI_STRING_DELIMITER
  1687.             )
  1688.             {
  1689.                 found = 0; // флаг, ключ не найден
  1690.                 break; // конец сравнения
  1691.             }
  1692.         }
  1693.  
  1694.  
  1695.         if ( found != 0 ) // если указанный ключ НАЙДЕН в файле
  1696.         {
  1697.             // если найдено совпадение не целого ключа файла, а его окончания с указанным ключом
  1698.             if ( curFilePos >= 0 )
  1699.             {
  1700.                 switch ( _ini_szFileContent[nFilePointer]{curFilePos} )
  1701.                 {
  1702.                     case INI_KEY_STARTS : {}
  1703.                     default: continue;
  1704.                 }
  1705.             }
  1706.  
  1707.  
  1708.             // ----------------
  1709.             // текущая позиция в файле будет на 1 больше текущей позиции INI_DELIMITER
  1710.             curFilePos = _ini_nDelimPos[nFilePointer][kPos] + 1;
  1711.  
  1712.             // ищем позицию начала значения, она будет помещена в curFilePos
  1713.             for ( ; ; curFilePos++ )
  1714.             {
  1715.                 if ( curFilePos >= _ini_nFileSize[nFilePointer] ) break;
  1716.  
  1717.                 switch ( _ini_szFileContent[nFilePointer]{curFilePos} )
  1718.                 {
  1719.                     case INI_SPACE_CHAR :   continue; // если это пробельный символ, перейдем к след. символу
  1720.                     default :               break; // если это другой символ
  1721.                 }
  1722.             }
  1723.             // ----------------
  1724.  
  1725.  
  1726.             // ----------------
  1727.             new szKeyValue[INI_FLOAT_SIZE]; // создадим строку для хранения дробного значения
  1728.             format( szKeyValue, INI_FLOAT_SIZE, "%f", fKeyValue ); // поместим строковое значение fKeyValue в szKeyValue
  1729.             // ----------------
  1730.  
  1731.  
  1732.             // ----------------
  1733.             new nValueLen = strlen(szKeyValue); // узнаем размер значения указанного ключа
  1734.  
  1735.             // если будущий размер файла превышает лимит
  1736.             if ( ( curFilePos + nValueLen ) >= INI_MAX_FILE_SIZE )
  1737.                 return INI_TOO_LARGE_FILE; // вернем код ошибки о переполнении файла
  1738.             // ----------------
  1739.  
  1740.  
  1741.             // ----------------
  1742.             new fileValueStartPos = curFilePos; // сохраним позицию начала значения
  1743.  
  1744.             // ищем позицию конца значения, она будет помещена в curFilePos
  1745.             for ( ; ; curFilePos++ )
  1746.             {
  1747.                 if ( curFilePos >= _ini_nFileSize[nFilePointer] ) break;
  1748.  
  1749.                 switch ( _ini_szFileContent[nFilePointer]{curFilePos} )
  1750.                 {
  1751.                     case INI_NUMBER_ENDS :  break; // если это конец числа - стоп
  1752.                     default :               continue; // если это другой символ, перейдем к след. символу
  1753.                 }
  1754.             }
  1755.  
  1756.             // вычислим смещение оставшихся позиций для INI_DELIMITER после изменения значения
  1757.             new filePosOffset = nValueLen - ( /*текущ длина значения*/ curFilePos - fileValueStartPos );
  1758.             // ----------------
  1759.  
  1760.  
  1761.             if ( _ini_nFileBusy[nFilePointer] != 0 ) // если прямо сейчас файл изменяется другой функцией
  1762.                 return INI_WRITE_ERROR; // вернем код ошибки при записи в файл
  1763.  
  1764.  
  1765.             // ----------------
  1766.             _ini_nFileBusy[nFilePointer] = 1; // флаг: файл изменяется = 1
  1767.  
  1768.             if ( filePosOffset != 0 ) // если длины старого и нового значений разные
  1769.             {
  1770.                 if ( filePosOffset < 0 ) // если длина нового значения меньше
  1771.                 {
  1772.                     for // копируем символы, стоящие после текущего значения на их новые места,
  1773.                     ( // которые теперь будут ближе к началу файла
  1774.                         ;
  1775.                         curFilePos < _ini_nFileSize[nFilePointer];
  1776.                         curFilePos++
  1777.                     )
  1778.                         _ini_szFileContent[nFilePointer]{ curFilePos + filePosOffset } =
  1779.                             _ini_szFileContent[nFilePointer]{curFilePos};
  1780.                 }
  1781.                 else if ( filePosOffset > 0 ) // если длина нового значения больше
  1782.                 {
  1783.                     new fileValueEndPos = curFilePos;
  1784.  
  1785.                     for // копируем символы, стоящие после текущего значения на их новые места,
  1786.                     ( // начиная с конца файла
  1787.                         curFilePos = _ini_nFileSize[nFilePointer] - 1;
  1788.                         curFilePos >= fileValueEndPos;
  1789.                         curFilePos--
  1790.                     )
  1791.                         _ini_szFileContent[nFilePointer]{ curFilePos + filePosOffset } =
  1792.                             _ini_szFileContent[nFilePointer]{curFilePos};
  1793.                 }
  1794.  
  1795.                 // изменим позиции всех INI_DELIMITER, которые находились после текущего INI_DELIMITER
  1796.                 for ( kPos++;  kPos < _ini_nKeysCount[nFilePointer];  kPos++ )
  1797.                     _ini_nDelimPos[nFilePointer][kPos] += filePosOffset;
  1798.  
  1799.                 _ini_nFileSize[nFilePointer] += filePosOffset; // изменим размер файла
  1800.                 _ini_szFileContent[nFilePointer]{ _ini_nFileSize[nFilePointer] } = 0; // конец строки для конца файла
  1801.             }
  1802.  
  1803.             curFilePos = fileValueStartPos; // текущ поз файла = поз начала старого значения
  1804.             for // запишем новое значение поверх старого
  1805.             (
  1806.                 new valuePos = 0;
  1807.                 valuePos < nValueLen;
  1808.                 curFilePos++, valuePos++
  1809.             )
  1810.                 _ini_szFileContent[nFilePointer]{curFilePos} = szKeyValue[valuePos];
  1811.  
  1812.             _ini_nFileChanged[nFilePointer]     = 1; // флаг: файл изменен = 1
  1813.             _ini_nFileBusy[nFilePointer]        = 0; // флаг: файл изменяется = 0
  1814.  
  1815.             return INI_OK;
  1816.             // ----------------
  1817.         }
  1818.     }
  1819.     // ----------------
  1820.  
  1821.  
  1822.     //
  1823.     // если указанный ключ НЕ НАЙДЕН в файле
  1824.     //
  1825.  
  1826.     // ----------------
  1827.     new szKeyValue[INI_FLOAT_SIZE]; // создадим строку для хранения дробного значения
  1828.     format( szKeyValue, INI_FLOAT_SIZE, "%f", fKeyValue ); // поместим строковое значение fKeyValue в szKeyValue
  1829.     // ----------------
  1830.  
  1831.  
  1832.     // ----------------
  1833.     new nValueLen = strlen(szKeyValue); // узнаем размер значения ключа
  1834.  
  1835.     // если будущий размер файла превышает лимит
  1836.     if ( ( _ini_nFileSize[nFilePointer] + 5 + nKeyLen + nValueLen ) >= INI_MAX_FILE_SIZE )
  1837.         return INI_TOO_LARGE_FILE; // вернем код ошибки о переполнении файла
  1838.     // ----------------
  1839.  
  1840.  
  1841.     // ----------------
  1842.     if ( _ini_nFileBusy[nFilePointer] != 0 ) // если прямо сейчас файл изменяется другой функцией
  1843.         return INI_WRITE_ERROR; // вернем код ошибки при записи в файл
  1844.  
  1845.     _ini_nFileBusy[nFilePointer] = 1; // флаг: файл изменяется = 1
  1846.     // ----------------
  1847.  
  1848.  
  1849.     // ----------------
  1850.     if // если последний символ файла не является разделителем строк
  1851.     (
  1852.             _ini_nFileSize[nFilePointer] > 0
  1853.         &&  _ini_szFileContent[nFilePointer]{ _ini_nFileSize[nFilePointer] - 1 } != INI_STRING_DELIMITER
  1854.     )
  1855.     {
  1856.         // вставим в конец файла перевод каретки и разделитель строк
  1857.         _ini_szFileContent[nFilePointer]{ _ini_nFileSize[nFilePointer] }        = '\r';
  1858.         _ini_szFileContent[nFilePointer]{ _ini_nFileSize[nFilePointer] + 1 }    = INI_STRING_DELIMITER;
  1859.  
  1860.         _ini_nFileSize[nFilePointer] += 2; // увеличим размер файла на 2
  1861.     }
  1862.  
  1863.     new curFilePos, strPos; // будут временным хранилищами позиций в файле/ключе/значении
  1864.  
  1865.     for // добавим посимвольно имя ключа в файл
  1866.     (
  1867.         curFilePos = _ini_nFileSize[nFilePointer], strPos = 0;
  1868.         strPos < nKeyLen;
  1869.         curFilePos++, strPos++
  1870.     )
  1871.         _ini_szFileContent[nFilePointer]{curFilePos} = szKeyName[strPos];
  1872.  
  1873.     _ini_szFileContent[nFilePointer]{ curFilePos++ }                = ' '; // после ключа добавим пробел
  1874.  
  1875.     _ini_nDelimPos[nFilePointer][ _ini_nKeysCount[nFilePointer] ]   = curFilePos; // добавим новую позицию INI_DELIMITER
  1876.     _ini_nKeysCount[nFilePointer]++; // кол-во ключей в файле +1
  1877.  
  1878.     _ini_szFileContent[nFilePointer]{ curFilePos++ }                = INI_DELIMITER; // после пробела выше добавим INI_DELIMITER
  1879.     _ini_szFileContent[nFilePointer]{ curFilePos++ }                = ' '; // после INI_DELIMITER добавим пробел
  1880.  
  1881.     // добавим посимвольно значение ключа в файл
  1882.     for ( strPos = 0;  strPos < nValueLen;  curFilePos++, strPos++ )
  1883.         _ini_szFileContent[nFilePointer]{curFilePos} = szKeyValue[strPos];
  1884.  
  1885.     _ini_szFileContent[nFilePointer]{curFilePos}    = 0; // добавим символ конца строки
  1886.     _ini_nFileSize[nFilePointer]                    = curFilePos; // изменим размер файла
  1887.     _ini_nFileChanged[nFilePointer]                 = 1; // флаг: файл изменен = 1
  1888.     _ini_nFileBusy[nFilePointer]                    = 0; // флаг: файл изменяется = 0
  1889.  
  1890.     return INI_OK;
  1891.     // ----------------
  1892. }
  1893.  
  1894.  
  1895.  
  1896.  
  1897. /*
  1898.     Удаляет из открытого INI файла указанный ключ и его значение.
  1899.  
  1900.     ПОДРОБНЕЕ
  1901.         Парсер ищет в ОЗУ в контенте файла указанный ключ и удаляет найденную
  1902.         пару ключ/значение.
  1903.  
  1904.         Имя ключа/значение в файле может быть любой длины,
  1905.         а также может содержать любые символы, кроме 2 символов конца строки \r и \n.
  1906.  
  1907.         Если эти символы есть в имени ключа или в значении,
  1908.         вы должны должны хорошо знать и осознавать последствия.
  1909.  
  1910.     ПАРАМЕТРЫ:
  1911.         nFilePointer        ИД_открытого_файла, полученный от ini_openFile / ini_createFile
  1912.         szKeyName[]         имя ключа
  1913.  
  1914.     ВЕРНЕТ:
  1915.         код ошибки < 0 или 0 при успехе
  1916. */
  1917.  
  1918. stock ini_removeKey ( nFilePointer, szKeyName[] )
  1919. {
  1920.     // ----------------
  1921.     if // если ИД открытого файла указан неверно
  1922.     (
  1923.             nFilePointer < 0
  1924.         ||  nFilePointer >= INI_MAX_OPENED_FILES
  1925.         ||  _ini_nSlotUsed[nFilePointer] != 1
  1926.     )
  1927.         return INI_WRONG_SLOT; // вернуть код: неверный указатель на открытый файл
  1928.     // ----------------
  1929.  
  1930.  
  1931.     // ----------------
  1932.     new nKeyLen = strlen(szKeyName); // узнаем длину имени указанного ключа
  1933.  
  1934.     if ( nKeyLen <= 0 ) // если указан пустой ключ
  1935.         return INI_KEY_NOT_FOUND;
  1936.     // ----------------
  1937.  
  1938.  
  1939.     // ----------------
  1940.     for // перебор и сравнение всех ключей файла с указанным ключом
  1941.     (
  1942.         new kPos = 0, curFilePos, found;
  1943.         kPos < _ini_nKeysCount[nFilePointer];
  1944.         kPos++
  1945.     )
  1946.     {
  1947.         // ----------------
  1948.         found = 0; // флаг, найдена ли позиция конца ключа = 0
  1949.  
  1950.         for // ищем позицию конца ключа
  1951.         (
  1952.             curFilePos = _ini_nDelimPos[nFilePointer][kPos] - 1; // текущ. поз. файла = поз. текущ. INI_DELIMITER - 1
  1953.             curFilePos >= 0; // продолжать пока поз. файла >= 0
  1954.             curFilePos-- // после каждого повтора текущ. поз. файла -= 1
  1955.         )
  1956.         {
  1957.             switch ( _ini_szFileContent[nFilePointer]{curFilePos} ) // узнаем что за символ в текущ. поз. файла
  1958.             {
  1959.                 case INI_SPACE_CHAR :           continue; // если это пробельный символ, перейдем к пред. символу файла
  1960.                 case INI_STRING_DELIMITER :     break; // если это конец строки
  1961.                 default : // если это другой символ
  1962.                 {
  1963.                     found = 1; // позиция конца ключа найдена
  1964.                     break; // конец цикла
  1965.                 }
  1966.             }
  1967.         }
  1968.  
  1969.         // если позиция конца ключа не найдена, переход к след. позиции INI_DELIMITER
  1970.         if ( found != 1 ) continue;
  1971.         // ----------------
  1972.  
  1973.  
  1974.         // сравниваем посимвольно текущий ключ файла и указанный ключ
  1975.         for ( new curKeyPos = nKeyLen - 1;  curKeyPos >= 0;  curFilePos--, curKeyPos-- )
  1976.         {
  1977.             if
  1978.             (
  1979.                     curFilePos < 0 // если поз файла стала < 0
  1980.                 ||  _ini_szFileContent[nFilePointer]{curFilePos} != szKeyName[curKeyPos] // если символы из ключей не равны
  1981.                 ||  _ini_szFileContent[nFilePointer]{curFilePos} == INI_STRING_DELIMITER // если символ из ключа это INI_STRING_DELIMITER
  1982.             )
  1983.             {
  1984.                 found = 0; // флаг, ключ не найден
  1985.                 break; // конец сравнения
  1986.             }
  1987.         }
  1988.  
  1989.  
  1990.         if ( found != 0 ) // если указанный ключ НАЙДЕН в файле
  1991.         {
  1992.             // если найдено совпадение не целого ключа файла, а его окончания с указанным ключом
  1993.             if ( curFilePos >= 0 )
  1994.             {
  1995.                 switch ( _ini_szFileContent[nFilePointer]{curFilePos} )
  1996.                 {
  1997.                     case INI_KEY_STARTS : {}
  1998.                     default: continue;
  1999.                 }
  2000.             }
  2001.  
  2002.  
  2003.             // сохраним позицию начала ключа
  2004.             new keyStartPos = ( curFilePos <= 0 ) ? 0 : curFilePos + 1;
  2005.  
  2006.  
  2007.             // ----------------
  2008.             // текущая позиция в файле будет на 1 больше текущей позиции INI_DELIMITER
  2009.             curFilePos = _ini_nDelimPos[nFilePointer][kPos] + 1;
  2010.  
  2011.             // ищем позицию начала значения, она будет помещена в curFilePos
  2012.             for ( ; ; curFilePos++ )
  2013.             {
  2014.                 if ( curFilePos >= _ini_nFileSize[nFilePointer] ) break;
  2015.  
  2016.                 switch ( _ini_szFileContent[nFilePointer]{curFilePos} )
  2017.                 {
  2018.                     case INI_SPACE_CHAR :   continue; // если это пробельный символ, перейдем к след. символу
  2019.                     default :               break; // если это другой символ
  2020.                 }
  2021.             }
  2022.             // ----------------
  2023.  
  2024.  
  2025.             // ----------------
  2026.             // ищем позицию конца значения, она будет помещена в curFilePos
  2027.             for ( ; ; curFilePos++ )
  2028.             {
  2029.                 if ( curFilePos >= _ini_nFileSize[nFilePointer] ) break;
  2030.  
  2031.                 switch ( _ini_szFileContent[nFilePointer]{curFilePos} )
  2032.                 {
  2033.                     case INI_STRING_ENDS :  break; // если это конец строки - стоп
  2034.                     default :               continue; // если это другой символ, перейдем к след. символу
  2035.                 }
  2036.             }
  2037.  
  2038.             // вычислим смещение оставшихся позиций для INI_DELIMITER после изменения значения
  2039.             new filePosOffset = keyStartPos - curFilePos;
  2040.             // ----------------
  2041.  
  2042.  
  2043.             if ( _ini_nFileBusy[nFilePointer] != 0 ) // если прямо сейчас файл изменяется другой функцией
  2044.                 return INI_WRITE_ERROR; // вернем код ошибки при записи в файл
  2045.  
  2046.  
  2047.             // ----------------
  2048.             _ini_nFileBusy[nFilePointer] = 1; // флаг: файл изменяется = 1
  2049.  
  2050.             // затираем контент ключа, следующими за ним данными файла
  2051.             for ( ; curFilePos <= _ini_nFileSize[nFilePointer]; curFilePos++ )
  2052.                 _ini_szFileContent[nFilePointer]{ curFilePos + filePosOffset } =
  2053.                     _ini_szFileContent[nFilePointer]{curFilePos};
  2054.  
  2055.             // затираем текущий INI_DELIMITER, и меняем позиции последующих INI_DELIMITER
  2056.             for ( ;  kPos < _ini_nKeysCount[nFilePointer];  kPos++ )
  2057.                 _ini_nDelimPos[nFilePointer][kPos] = _ini_nDelimPos[nFilePointer][kPos + 1] + filePosOffset;
  2058.  
  2059.             _ini_nKeysCount[nFilePointer]--;
  2060.             _ini_nFileSize[nFilePointer] += filePosOffset; // изменим размер файла
  2061.             _ini_szFileContent[nFilePointer]{ _ini_nFileSize[nFilePointer] } = 0; // конец строки для конца файла
  2062.  
  2063.             _ini_nFileChanged[nFilePointer]     = 1; // флаг: файл изменен = 1
  2064.             _ini_nFileBusy[nFilePointer]        = 0; // флаг: файл изменяется = 0
  2065.  
  2066.             return INI_OK;
  2067.             // ----------------
  2068.         }
  2069.     }
  2070.     // ----------------
  2071.  
  2072.  
  2073.     //
  2074.     // если указанный ключ НЕ НАЙДЕН в файле
  2075.     //
  2076.  
  2077.     // ----------------
  2078.     return INI_KEY_NOT_FOUND;
  2079.     // ----------------
  2080. }
  2081.  
  2082.  
  2083.  
  2084.  
  2085. /*
  2086.     Возвращает строку с описанием указанного кода ошибки на русском языке.
  2087.  
  2088.     ПОДРОБНЕЕ
  2089.         Если какая-то функция вам вернула не 0 (всё в порядке), а отрицательное
  2090.         значение, значит, имела место ошибка. Если вы хотите вывести в любой лог или
  2091.         в диалог человеческое описание этой ошибки, то передайте этой функции код
  2092.         ошибки и она вам вернет описание этой ошибки на русском языке.
  2093.  
  2094.         Описание ошибок довольно длинные и превышают размер в 128 символов,
  2095.         поэтому выводить эти описания в чате не рекомендуется!
  2096.  
  2097.     ПАРАМЕТРЫ:
  2098.         nErrorCode      код ошибки, полученный от любой функции
  2099.  
  2100.     ВЕРНЕТ:
  2101.         строку с человеческим описанием ошибки, под указанным кодом
  2102.  
  2103.     ПРИМЕР:
  2104.         new ini = ini_openFile("несуществующий файл"); // пытаемся открыть несуществующий файл
  2105.  
  2106.         if ( ini < 0 ) print( ini_getErrorInfo(ini) );
  2107.         else
  2108.         {
  2109.             new number, result = ini_getInteger( ini, "несуществующий ключ", number ); // пробуем получить значение несуществующего ключа
  2110.  
  2111.             if ( result < 0 ) print( ini_getErrorInfo(result) );
  2112.  
  2113.             ini_closeFile(ini);
  2114.         }
  2115. */
  2116.  
  2117. stock ini_getErrorInfo ( nErrorCode )
  2118. {
  2119.     new error_ms[200] = "Ошибок нет";
  2120.  
  2121.     if ( nErrorCode >= 0 ) return error_ms;
  2122.  
  2123.     switch ( nErrorCode )
  2124.     {
  2125.         case INI_FILE_NOT_FOUND :     error_ms = "INI файл, указанный в `ini_openFile`, не существует, возможно, указан неверный путь к нему";
  2126.         case INI_FILE_ALREADY_EXIST : error_ms = "INI файл, указанный в `ini_createFile`, уже существует, укажите другое имя/путь к файлу";
  2127.         case INI_TOO_LARGE_FILE :     error_ms = "Невозможно добавить новый ключ в INI файл, т.к. размер INI файла превысит допустимый лимит в " #INI_MAX_FILE_SIZE " символ(ов), или будет превышен лимит в " #INI_MAX_KEYS_IN_FILE " ключей";
  2128.         case INI_WRONG_PATH_SIZE :    error_ms = "Длина пути к INI файлу, указанного в `ini_openFile` / `ini_createFile`, превышает лимит в " #INI_MAX_FILENAME_SIZE " символ(ов)";
  2129.         case INI_READ_ERROR :         error_ms = "Ошибка при чтении INI файла с диска, возможно, файл занят другим процессом";
  2130.         case INI_WRITE_ERROR :        error_ms = "Ошибка при записи содержимого INI файла на диск, возможно, файл занят другим процессом";
  2131.         case INI_NO_FREE_SLOT :       error_ms = "Лимит в " #INI_MAX_OPENED_FILES " одновременно открытых INI файлов исчерпан, поэтому открыть/создать еще 1 файл нельзя, пока не закрыт хоть 1 из открытых файлов";
  2132.         case INI_WRONG_SLOT :         error_ms = "Указан неверный ID открытого INI файла, возможно, при открытии INI файла произошла ошибка, поэтому вместо ID файла вы получили код ошибки";
  2133.         case INI_KEY_NOT_FOUND :      error_ms = "Указанный ключ не найден в открытом INI файле";
  2134.         case INI_WRONG_RETURN_SIZE :  error_ms = "При попытке чтения строкового значения из INI файла, последний параметр `nSizeOfReturn` в `ini_getString` оказался <= 0, укажите вручную значение > 0 для него";
  2135.  
  2136.         default:                      error_ms = "Неизвестная ошибка";
  2137.     }
  2138.  
  2139.     return error_ms;
  2140. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement