Armazenando dados em arquivos INI

Introdução:
Podemos, de várias formas, manipular valores e expressões de texto em arquivos externos, tanto em MMF2 como em programas similares. Podemos armazenar tais dados em arquivos de texto, DATs, e, principalmente, em arquivos INI. Para explicar melhor, utilizarei a definição DESTE site:

"Um arquivo INI é um arquivo texto usado para armazenar/fornecer configurações pessoais para sistemas/usuários."

Isso significa que podemos "gravar" ou "receber" dados em tais arquivos. Tais dados podem ser tanto valores (1, 5, 10 etc) como conjuntos de caracteres ("Tiago", "armazenar dados em arquivos INI" etc). Tais configurações pessoais podem ser alteradas tanto por um programa (no nosso caso, MMF2), quanto pelo usuário. Ou seja, ou um programa modifica/recebe tais dados automaticamente, ou um usuário pode modificar tais valores à bel-prazer.

"Mas para quê isto nos serve na criação de jogos?", você me pergunta. Armazenar dados em arquivos externos serve-nos para que não perdamos tais dados ao fechar ou reiniciar o programa/jogo. No caso do MMF2, manda-se armazenar os dados no INI em determinada condição, e receber tais dados em outra. Podemos utilizar isto para gravar posições, valores, vidas etc. Ou seja, fazer um Sistema de Salvar (Save Game).

Segue este exemplo. Estou fazendo um jogo de labirinto. Coloco um botão de salvar no topo do Frame, e programo para que, se o jogador clicar no botão, armazenar as posições X e Y do herói em um arquivo INI. Então, coloco para que assim que começar o Frame, definir as posições X e Y do herói para as que estão armazenadas no INI. Assim, se eu salvar, quando posteriormente abrir o jogo, o personagem estará nas posições anteriores.

Só existe um problema: o usuário (no caso, o jogador) pode editar manualmente os dados de um INI. O que significa que ele pode simplesmente abrir o INI e modificar seus valores, a fim de trapacear. É algo que pode ser contornado, claro, mas fica o aviso, para que não deixe arquivos INIs importantes do jogo (quantidade de dinheiro do jogador, HP etc) rolando pela pasta dele. Assim, o jogador concluiria o jogo facilmente.

Existe uma solução simples: "mascarar" o INI, colocando nele outra extensão, como (*.dat), (*.lua), (*.sav), ou até mesmo extensões mais incomuns como (*.dll) e (*.arquivo_INI_camuflado_do_meu_jogo). Ou seja, fazer um arquivo de texto, com a estrutura de um INI, só que salvando com outra extensão. Nada de outro mundo. A extensão que será utilizada para mascarar o INI realmente não importa... a não ser que você seja estúpido o suficiente para usar o último exemplo. Podemos, também, criptografar o INI: assim, você nem precisa dar-se ao trabalho de mascará-lo, pois o jogador não conseguirá modificar seus valores. ;)

Perdoem-me pela grande introdução, mas acredito que seja importante não apenas dar-lhes o conceito de INI, mas falar sobre eles na criação de jogos.

Estrutura de um arquivo INI:
Para fazer um arquivo INI, basta abrir o Bloco de Notas, criá-lo, e salvar como nome_do_arquivo.ini, com a opção Todos os arquivos em vez de Documentos de texto (*.txt). A estrutura de um INI é a seguinte:

[GRUPO]
ITEM=DADO

Os [ ] (colchetes) declaram um GRUPO. Todo INI deve possuir no mínimo um. Você pode armazenar todos os valores em um único grupo, mas por uma questão de organização, isso não é aconselhável. Os itens compreendidos entre a próxima linha depois da declaração de um grupo X e uma linha antes da declaração de um grupo Y, pertencem grupo X. Grupos devem ser separados de ITENS ou de outros GRUPOS por linhas. Um GRUPO pode possuir tanto caracteres normais, quanto especiais (sinais e acentos), espaços e números. Mas cuidado, pois tais caracteres fazem sim a diferença (o grupo Herói é diferente de Heroi).

Cada ITEM armazena um dado, que pode ser um valor ou conjunto de caracteres (se fôssemos usar o INI acima do jeito que está, o valor do item ITEM seria o conjunto de caracteres DADO). Itens devem ser separados por linhas. Cada linha deve conter um GRUPO ou um ITEM. E cada GRUPO ou ITEM deve ser expresso em uma única linha. Ao saltar uma linha, você não estará mais trabalhando com aquele GRUPO ou ITEM. Um ITEM pode possuir tanto caracteres normais, quanto especiais (sinais e acentos), espaços e números. Mas cuidado, pois tais caracteres fazem sim a diferença (o item Mangas é diferente do item Mangás).

O sinal de = (igual) define o valor de um ITEM para o que vier depois dele naquela linha.

O que vem depois de um sinal de = em uma linha é o DADO de um determinado ITEM. Um DADO pode ser um valor numérico (value) ou um conjunto de caracteres (string). Não há diferença alguma entre tais dados, levando em consideração a declaração deles. Se o DADO possuir apenas caracteres numéricos após o sinal de igual, ele será um value; já se ele possuir também caracteres não-numéricos (espaços, letras e sinais), ou apenas estes tipos de caracteres, ele será uma string.

GRUPOS e ITENS não fazem diferença em relação à letras minúsculas e maiúsculas; DADOS, porém, fazem.

Aqui vai um exemplo de INI, agora, com mais de um grupo/itens:
[Personagem]
posX=286
posY=242
[Inimigo]
posX=491
posY=493

Este arquivo armazena as posições X e Y (cada uma determinada por um ITEM) do personagem e de seu inimigo. Perceba que eu posso utilizar dois itens com o mesmo nome, se estiverem em grupos diferentes. Caso eu use dois ou mais ITENS com o mesmo nome em um mesmo grupo, ele considerará o último a aparecer (mais recente, do ponto de vista do arquivo). E se eu usar dois ou mais GRUPOS com o mesmo nome, o programa "mesclará" os grupos.

Ini++:
Agora que já conhecemos a estrutura de um arquivo INI e como criá-los, veremos como armazenar e receber dados em um programa de MMF2 com tais arquivos. Para isto, utilizaremos a extensão Ini++, desenvolvida por Jack Webster.

Nas propriedades desta extensão, encontramos logo de cara uma opção Default file, para utilizar um arquivo INI padrão: isto significa que será o primeiro arquivo INI procurado pela extensão. Se desmarcar esta caixa, você precisará carregar o INI manualmente; se deixar marcada, ele o carregará de imediato.

A opção Read only impede que o programa mude os valores do INI padrão; o que significa que ele apenas poderá receber os valores desse INI. Isto serve para que o usuário possa alterar dados pelo INI, e, estes dados, seriam carregados pelo programa. Opção desnecessária, eu diria.

Base folder é o diretório onde se encontra o arquivo INI padrão. Você pode optar, principalmente, entre a pasta raíz do programa e a pasta WINDOWS, que se encontra no Disco local C:\.

E essas são todas as opções realmente relevantes. Perceba que, se o INI padrão ainda não existir, a extensão há de criá-lo. Nesse caso, os valores definidos pelos eventos criarão no INI os grupos e itens. Se o INI já existir, ele simplesmente o carregará. Prefiro definir logo os valores que quero usar, então, sempre crio o INI antes. Em relação à ordem, não faz diferença, quando mandamos o programa armazenar valores, ele organiza os itens em ordem alfabética.

Armazenando/recebendo dados no MMF2:
Uma vez que a extensão esteja configurada como desejar, vamos aprender a armazenar e receber valores de arquivos INI no MMF2.

Antes de aprender a receber, vejamos como armazenar dados em um INI. Utilizamos ações diferentes para armazenar valores e strings. Para armazenar um valor, na ação do evento desejado, vá na classe da extensão, entre em Setting Items, e escolha Set Value.

A primeira coisa que ele irá pedir é o grupo que contém o item no qual o dado será armazenado. Entre aspas, digite o nome do grupo e dê OK.

Grupo Herói.

Então, ele pedirá o item no qual o dado será armazenado. Novamente entre aspas, escreva o nome do item e dê OK.

Item posX.

Agora, ele quer saber se o valor a ser armazenado é inteiro ou racional. Se for um número inteiro, deixe como 0. Se for um número racional, coloque 1. Os números racionais também englobam números inteiros; isso significa que se você colocar 1, ele armazenará tanto valores inteiros quanto racionais no item, apesar do contrário não ser verdade (colocando 0, ele apenas armazenará números inteiros). Acredito que tal divisão sirva para, no primeiro caso, ocupar menos bytes na memória do PC. Mas não sei explicar-lhes com certeza. É assim no C/C++, não sei se isto se aplica aqui. Enfim, após escolher o tipo de valor, dê OK.

Armazenar valor inteiro.

Por fim, ele quer saber qual o valor a ser armazenado. Tal valor pode ser fixo ou recebido de outro objeto (alterable values, global values etc). No exemplo, o valor armazenado deverá ser a posição X do personagem.

Receber posição X do Herói.

E, assim, armazena-se um valor. Vamos, então, aprender a armazenar uma string. É basicamente a mesma coisa, não fosse o fato de ser mais rápido, pois não existem strings inteiras ou racionais. Para armazenar uma string, na ação do evento desejado, vá na classe da extensão e entre em Setting Items, e escolha Set String.

Novamente, ele primeiro pede o grupo que contém o item no qual o dado (dessa vez uma string) será armazenado. Escreva entre aspas, e dê OK.

Grupo Herói.

Aqui, ele pede o item no qual a string será armazenada. Escreva entre aspas e dê OK.

Item nome.

Por fim, aqui, ele pede a string a ser armazenada. Uma string, tal como um valor, pode ser fixa ou recebida de outro objeto. Na imagem, a string armazenada será recebida do texto contido na EditBox.

Armazenar como string o texto da EditBox.

E isso é o básico sobre armazenar dados. Vamos, agora, aprender a receber dados.

--//--

Na ação, defina o valor/string desejado como definiria normalmente (por exemplo, se for para definir o valor de um Counter, vá em Set Counter). Quando ele pedir o valor, vá em Retrieve data from an object. Dependendo do tipo de dado (valor ou string), selecione Get Item Value (group, item, default value) ou Get Item String (group, item, default string).

Se o dado for um valor, dê um duplo clique sobre a extensão e escolha Get Item Value (group, item, default value). Deve aparecer a seguinte expressão:
GetItemValue( "Ini++", > Enter string here <, > Enter string here <, > Enter value here <)

Expressão que deve aparecer.

Substitua o primeiro > Enter string here < pelo nome do grupo (entre aspas), que contém o item no qual o valor a ser recebido está. O segundo > Enter string here <, substitua, também entre aspas, pelo nome do item no qual o valor em questão está. E, por fim, você deve substituir o terceiro > Enter string here < por um valor padrão, que o programa utilizará, caso ele não encontre o item em questão; seja por não existir, ou por ainda não ter sido criado (caso seja o programa que vá criá-lo posteriormente).

Receber o valor do item posX, que está no grupo Herói. Caso o item não seja encontrado, adota-se o valor 0.

Já se o dado for uma string, dê um duplo clique sobre a extensão e escolha a opção Get Item String (group, item, default string). A expressão que deve aparecer agora é esta:
GetItemString$( "Ini++", > Enter string here <, > Enter string here <, > Enter string here <)

Expressão que deverá aparecer.

Substitua o primeiro > Enter string here < pelo nome do grupo (entre aspas) que contém o item no qual a string em questão está armazenada. O segundo > Enter string here <, você deve substituir pelo nome do item (também entre aspas) no qual a string a ser recebida está armazenada. Por fim, o terceiro > Enter string here < nada mais é do que uma string padrão (que também deve ser escrita entre aspas) que o programa adotará caso o item não exista, ou ainda não tenha sido criado (no caso de o programa criá-lo apenas futuramente).

Receber a string do item nome, que pertence ao grupo Herói. Caso o item não seja encontrado, utilizar como valor padrão um texto vazio.

--//--

E isso é o básico sobre armazenar e receber dados de um arquivo INI ;). A extensão ainda possui algumas funções necessárias e úteis (como mudar o INI com o qual ela está trabalhando), mas não vou deixar a matéria muito extensa explicando-as. Você pode explorar o resto da extensão sozinho; não é difícil. Fica isso como lição de casa.

Se quiser ver um exemplo que fiz para esta matéria, clique AQUI.

Bom, é isso. Espero que tenham entendido :). Dúvidas, sabem meu e-mail.
Flw.