Implementação do Itálico
PARTE 2Faz um bom tempo, tive que lembrar muita coisa.
É melhor acompanharem no IDA as modificações e as novas rotinas.
PlanejamentoRelembrando rapidamente, com o documento "MPEG4 Subtitle Display in ARM" (do NewAge), foi possível achar a rotina sub_3FB1A com o endereço base em que ficam gravadas as opções de legenda (#9EB88), a rotina responsável por exibir as legendas (que começa em #40348), a rotina responsável por ler os caracteres da legenda (que começa em #3FB56) e outras. Na rotina que lê os caracteres da legenda, é possível identificar o endereço base do buffer do quadro atual da legenda. Apenas 1 quadro é carregado no buffer por vez e seus caracteres são lidos um a um. O endereço base é #A4E70, sendo que na posição #A4E70+1 temos o total de caracteres do quadro e em #A4E70+2 temos o primeiro caractere do quadro.
Bom, como disse na
PARTE 1, o primeiro passo foi adicionar um offset aos valores lidos desse buffer para ver se uma fonte com mais de 256 caracteres podia ser usada para colocar os caracteres itálicos. Essa foi uma das opções sugeridas no fórum do jmaraujo e também pelo zeurt. Como funcionou, passou a ser a idéia central do itálico.
O maior problema era que o modelo de chip S desse player originalmente não exibe as tags srt na legenda, e o buffer #A4E70 (por enquanto vou chamá-lo assim) já é preenchido sem as tags. Como eu disse antes, teria que haver alguma parte no ARM que carregasse o buffer eliminando as tags. Procurei várias vezes no código por rotinas que fizessem uso do endereço #A4E70 e que tivessem operações de comparação (CMP, SUB etc) com os bytes #3C ("<") e #3E (">"). Mas não conseguia achar. Porém, mexendo mais no IDA, descobri que havia certas regiões que os scripts do mt13x9 group não consideravam como que fossem código, mas que são código. Isso tem a ver com os ARM codes, dos quais vou falar um pouco mais a frente. Então, selecionei algumas partes e forcei o IDA a considerar como código. E pronto, lá estava a rotina que inibe as tags! Começa em #8CD44. Ela lê um outro buffer, que tem base em #9A610 (o qual vou chamar de buffer primário), e preenche o buffer #A4E70 (o qual passo a chamar de buffer secundário), com tudo que não estiver entre "<" e ">". O buffer primário, ao contrário do secundário, é pequeno. Cabe no máximo #20 (32) caracteres. Então ele é lido de 32 em 32 caracteres, os quais são transferidos para o buffer secundário, até que o fim do quadro seja encontrado. Cabe ressaltar que o buffer primário já está carregado apenas com os caracteres de texto da legendas. As posições de tempo já estão cortadas. Assim, ainda deve existir um pré-buffer que seleciona e corta as posições de tempo que estão no arquivo de legenda.
Agora, tinha que dar um jeito de repassar as opções das tags para o ARM. Eu pensei em usar a mesma opção de alinhamento horizontal (que chega ao ARM na posição #9EB88+9). Por quê? Porque ao fazer testes quando estava criando as opções do alinhamento da legenda, percebi que apenas o bit menos significativo era o que importava. Se repassamos o valor XXXXXXX1, o alinhamento é centralizado. Se for XXXXXXX0, o alinhamento é à esquerda. Assim, os outros bits podem ser usados para armazenar outras informações e teremos que usar operações lógicas tanto no 8032, para juntar as opções do alinhamento horizontal com as opções das tags srt, quanto no ARM, para separar essas mesmas informações. Usei o seguinte esquema para o byte do alinhamento horizontal:
ITTTXXXA,
em que I é a flag usada para indicar se o texto está em itálico, TTT são os 3 bits usados para as opções de tags (são 5 opções, precisamos de pelo menos 3 bits), XXX não interessa e A é o bit com a opção de alinhamento horizontal.
Houve um outro complicador nesse processo. Vou explicar para quem for verificar o código 8032 não ficar confuso. Como estava sem espaço no menu OTHERS para as opções de tags, eu juntei as opções de alinhamento horizontal e vertical numa única opção. Assim, quem olhar no 8032, verá que eu primeiro leio a EEPROM com a nova opção de alinhamento geral, separo-a nas opções de alinhamento vertical e horizontal, envio a opção de alinhamento vertical (que chega ao ARM em #9EB88+5), leio a EEPROM com a opção de exibição das tags, junto-a com a opção de alinhamento horizontal e envio ao ARM (que chega em #9EB88+9).
Para fazer as modificações no ARM, precisei de espaço no código. Tentei usar o famoso espaço das mensagens de erro, mas travava quando fazia a chamada lá dos endereços da rotina de inibição das tags. Era como se ela estivesse desconectada do restante do código. Então, percebi que o ARM também possui bancos, páginas separadas: os ARM codes. A rotina que exibe a legenda fica no ARM code 1, mesmo lugar das mensagens de erro. Já a rotina que inibe as tags e monta o buffer secundário fica no ARM code 5! Ainda não pesquisei como isso funciona, mas já deve ter sido revelado em algum documento do mtk group ou daqui do fórum. Não sei se há formas de pular de uma página a outra, se são independentes mesmo, pensando numa possível multitarefa, ou ainda se são carregados em outra ordem. De qualquer forma, para criar espaço, extraí o ARM code 5 com o MTK Remaker, acrescentei bytes ao final do código com um editor hexadecimal e o reinseri com o MTK Remaker. Assim, pude modificar a rotina que começa em #8CD44 e acrescentar a rotina sub_8E99C. Já para algumas modificações que fiz na rotina que lê o buffer secundário, pude utilizar tranquilamente o espaço das mensagens de erro, pois fica tudo no ARM code 1.
Explicando as ModificaçõesPrimeiro, no ARM code 5, o itálico ainda não é acionado. Apenas é feita a codificação das opções de tags. Em sub_8E99C, verifica-se qual a opção de tag o usuário escolheu para que se possa montar o buffer secundário da forma correta. Como a tabela de palavras que começa em #8CECC não possui o endereço #9EB88, criei uma tabela em #8EB84 apenas para fazer essa referência (o código ARM é realmente estranho para quem está acostumado com CISC). Assim, podemos ler o endereço #9EB88+9 e, com as instruções lógicas, decifrar as opções (o que está em TTT no byte ITTTXXXA). Por exemplo, se for "mostrar todas as tags" (#20=#B00100000), o controle é devolvido para a outra rotina, que preenche o buffer secundário sem fazer qualquer restrição. Se for "esconder todas", o controle é devolvido e tudo passa a ser executado como antes. Já nas demais opções, é feita uma coisa importante. Temos que verificar se o atual caractere é "<", se o próximo é "i" e se o seguinte é ">", ou se atual caractere é "<", o próximo é "/", o seguinte é "i" e, finalmente, o da posição atual + 3 é ">". Para o caso de "substituir por aspas", simplesmente trocamos o atual caractere por ", e o contador da posição do buffer primário é atualizado com o valor de R4. Assim, o buffer secundário é montado com as tags itálicas sendo substituídas por aspas. Quando a opção for "habilitar itálicos", é feita uma codificação que será interpretada apenas pela rotina que lê os caracteres do buffer secundário, no ARM code 1. Se a tag <i> for encontrada, será substituída pelo código #03. Se for a </i>, será substituída por #04.
Ao trabalhar com esse buffer primário, de no máximo 32 caracteres, temos um problema. Se a tag começar antes do caractere 32 e terminar depois, não há como detectá-la. Por exemplo, se um texto no formato <i>456789012345678901234567890</i> for lido, quando a rotina encontrar o caractere "<" da tag final, que está na posição 30 (começa em 0), vai tentar achar os demais. Mas não vai encontrar o "i", que está na posição 30+2=32, fora do buffer. Assim, não irá substituir essa tag nem por " e nem por #04. Por isso, dependendo de onde estiver a tag, muitas vezes o itálico permanecerá ligado. Quando o buffer primário acabava, tínhamos o registro da posição (#20) em #8CDC4 antes de chamar uma rotina em #39356 (essa rotina não é a mesma loc_39356 que o IDA mostra erradamente, pois ele está mostrando a rotina do ARM code 1, mas essa posição, no momento em que é chamada, fica em outro ARM code, e muito provavelmente está envolvida com o pré-buffer que falei anteriormente, pois é ela que "enche" o buffer primário a partir de uma posição repassada). A solução foi modificar essa posição para que seja igual ao atual contador (em R4). Se for encontrada uma tag, por exemplo, que começa na posição 31, ela não será identificada, pois ficará "quebrada". Mas, ao acabar o buffer, na posição 32, será repassada a posição 31 (início da tag), para a rotina #39356. Sim, vai haver um salto para trás e o novo preenchimento do buffer vai começar no que seria a posição 31 do preenchimento anterior (a posição 31 será lida novamente). Esse artifício só ocorre se uma tag for "quebrada" no final do buffer primário.
Bom, agora vamos às modificações na parte que exibe a legenda, no ARM code 1. A rotina que lê o buffer secundário da legenda é a sub_3FB56. Essa rotina é chamada várias vezes pela rotina de exibição de legendas (sub_40348) para poder verificar quantas linhas serão necessárias, para calcular alinhamento vertical de 1 ou 2 linhas, o alinhamento vertical de 3 linhas, 4 linhas, a centralização etc. Assim, se mudarmos diretamente a rotina, iremos adicionar novas operações que serão executadas várias e várias vezes. O melhor foi alterar onde exatamente é repassado o caractere para exibição. Isto é feito em #407AA. Como eliminei o "bug" da fonte repetida anteriormente, a rotina sub_3FB56 foi bastante reduzida, sobrando espaço para criar uma nova rotina, a sub_3FB5E. Daí, em #407AA chamamos essa nova rotina que vai carregar a opção de tag e chamar uma rotina criada na área de mensagens de erro: sub_58A38. Ela verifica se o atual código de caractere é 03 (início itálico). Se for, ela vai setar o bit 7 da posição #9EB88+9 (o bit I de ITTTXXXA). Se o atual código de caractere é 04, ela reseta o bit 7. Mais à frente, se o caractere não for nem 03 e nem 04, for maior que #20, se o bit 7 estiver setado e se, somente se, a opção atual de tags for "habilitar itálicos", ao caractere lido será adicionado um offset de #DF (223, pois não há itálicos abaixo de #20 e o total de caracteres das fontes é 256+223).
Isso foi o suficiente para começarem a aparecer os itálicos, porém tive ainda 2 problemas. O alinhamento horizontal centralizado e as legendas em que a tag de fechamento </i> não é usada. Para resolver o problema do não uso da tag </i>, tive que incluir na rotina de exibição dois trechos que cancelam o itálico no início da exibição de um novo quadro de legenda. Dois trechos, pois a rotina tem duas partes independentes: uma para quando o quadro tem 1 ou 2 linhas e outra para quando tem mais de 3 linhas. Um dos desvios para cancelar o itálico é feito em #40642 e o outro é feito em #406D2. Tentei fazer apenas um único desvio no início da rotina, mas nem sempre funcionava.
Já a rotina que calcula a centralização é a sub_3FB7A (deveria ser a que os scripts identificaram como SUB_CalcTextLineWidth__sub_3FC98, mas essa não tem uso e é muito parecida com a anterior; suspeito que seja utilizada para alinhar a legenda à direita, no caso de línguas com esse tipo de alinhamento, como o árabe). Os caracteres itálicos ficaram com largura um pouco maior, devido à angulação (na verdade, o espaço vazio aumentou, pois a área do caractere continua sendo um retângulo). Assim, uma linha em itálico será ligeiramente maior que uma linha normal, com os mesmos caracteres. Quando o texto está alinhado à esquerda, não há problemas. As linhas começam sempre na esquerda, sendo que as itálicas ficam maiores. Porém, se tivermos usando a opção de centralização, as palavras em itálico serão centralizadas como se não estivessem em itálico (pois a rotina sub_3FB7A chama a antiga rotina de leitura de caracteres e não a nova, que habilita os itálicos). Assim, como a linha com itálico é maior, ele fica deslocada um pouco mais à direita (por ser maior do que foi considerada pelo cálculo de centralização). A primeira solução que usei foi também chamar a nova rotina sub_3FB5E de dentro da rotina de centralização. A princípio funcionou, mas tinha o inconveniente de que toda vez que a centralização era chamada (a centralização é chamada 1 vez para quando temos 1 ou 2 linhas, 3 vezes para 3 linhas e 5 vezes para 4 linhas), a legenda era percorrida de verdade, fazendo com que o itálico fosse ativado antes da hora. Por exemplo, vejamos o seguinte quadro:
Primeira linha de teste.
Segunda linha de teste.
Terceira <i>linha de teste.
Quarta linha de teste.
O itálico deveria ser ligado após a tag e ficar assim até o fim do quadro. Porém, todo quadro estava ficando em itálico, pois a centralização da terceira linha (que ocorre antes da exibição de fato) liga o itálico e o mantém ligado (pois não encontra a tag </i>) antes de o quadro ser exibido.
A solução final foi modificar a rotina de centralização para que faça uma cópia da opção em #9EB88+9 antes de calcular a posição horizontal e, ao final, restaure a opção. Ou seja, se entramos na rotina com itálico desligado, saímos dela com o itálico desligado. Fazendo uma busca pelo código, não achei nenhuma referência à posição #9EB88+#0F. Assim, usei essa posição para guardar uma cópia de #9EB88+9 na rotina de centralização, que pode ser vista no IDA.
Bom, acho que é só isso que faltava
.