Initial commit

This commit is contained in:
Aurelien Labate 2019-12-26 12:07:17 +01:00
commit 0644716d15
8 changed files with 1170 additions and 0 deletions

275
DOCUMENTATION.md Normal file
View File

@ -0,0 +1,275 @@
# Problem solved by this tool
## sh3/sh4 compatible input function: `_GetKeyState`
Most of the problem solved by this tool are keyboard input related. So the first step was to create an sh3/sh4 compatible function to get keyboard key state. This function is injected at the end of the program and because of that we also need to rewrite the header, but that's easy thanks to Simon Lothar and his documentation.
You can find the full commented assembly source of the function into [_getKeyState.asm](./_getKeyState.asm).
This function is faster than most input functions (except KeyDown). So using the following code, I tested the speed of each functions first:
```c++
int AddIn_main(int isAppli, unsigned short OptionNum)
{
unsigned int timeBegin;
unsigned int duration;
char string[9];
int i;
while(1)
{
timeBegin = RTC_GetTicks();//RTC_GetTicks is a syscall documented in FxReverse
for(i=0;i<5000;i++)
{
key_down(K_EXE);//Change this function here
}
duration = RTC_GetTicks()-timeBegin;
intToHex(duration, string);
Bdisp_AllClr_DDVRAM();
locate(1,1);
Print((unsigned char*)string);
Bdisp_PutDisp_DD();
}
return 1;
}
void intToHex(unsigned int in, char* string)
{
string[0] = nibbleToHex((unsigned char)in>>28);
string[1] = nibbleToHex((unsigned char)(in>>24)&0xF);
string[2] = nibbleToHex((unsigned char)(in>>20)&0xF);
string[3] = nibbleToHex((unsigned char)(in>>16)&0xF);
string[4] = nibbleToHex((unsigned char)(in>>12)&0xF);
string[5] = nibbleToHex((unsigned char)(in>>8)&0xF);
string[6] = nibbleToHex((unsigned char)(in>>4)&0xF);
string[7] = nibbleToHex((unsigned char)in&0xF);
string[8] = 0;
}
char nibbleToHex(unsigned char in)
{
char out;
if(in <= 9)
out = 0x30 + (unsigned int)in;
else
{
switch(in-10)
{
case 0 : out = 0x61; break;
case 1 : out = 0x62; break;
case 2 : out = 0x63; break;
case 3 : out = 0x64; break;
case 4 : out = 0x65; break;
case 5 : out = 0x66; break;
}
}
return out;
}
```
And here are the result:
It shows the number of ticks taken to execute the function.
| | Original | _GetKeyState SH3 | _GetKeyState SH4 |
|--------------------------------------|----------|------------------|------------------|
| IsKeyDown | 0xb2 | 0x17(miss 155) | 0x11(miss 161) |
| IsKeyDown with 0x1000 waitloop | --- | 0x17b | 0x1cd |
| IsKeyDown ; sh3:0x12D1F ; sh4:0xF1A8 | --- | 0xb2 | 0xb2 |
| IsKeyUp | 0x1a43 | 0x17(miss 6700) | 0x11(miss 6706) |
| IsKeyUp ; sh3:0x12D1F ; sh4:0xF1A8 | --- | 0x1a42 | 0x1a4a |
| KeyDown | 0x9 | 0x11 | 0xd |
* 0x1000 loop takes 0x164 ticks to be executed on SH3 => 1024/89 loop/ticks
* 0x1000 loop takes 0x1BC ticks to be executed on SH4 => 1024/111 loop/ticks
* KeyDown is faster than `_GetKeyState`
## IsKeyDown function from FxLib
The IsKeyDown Function read directly the keyboard input state without using a syscall. But with power graphic 2 the keyboard connections to the CPU changed and it's not possible anymore to read the keyboard the same way.
As Casimo showed us with his solution in C, Power Graphic 2 calculators have a specific keyboard register that we can read easily. The solution used by this tool is it to lookup for the binary code of the IsKeyDown function, and replacing it with a new one that jump to `_GetKeyState`.
Firstly, we look for the `isKeyDown` function:
```
4f227ffc63f3bee52f367f0463f0633c43118b0384f1600c401189037f044f26000be000bf5664f37f044f26000b0009
```
Then we replace its content starting at offset `0xc` of the function. (The first bytes replaced are `0x63f0`).
```
_IsKeyDownReplacement
; before this injection, there is call of _KeyCodeConvert that put an array of two byte that respectivly contain the col and the line in the stack
; So we put them into registers to use them letter in the _GetKeyState function that we will call
mov r15,r4 ; first param of the _GetKeyState function
mov #1,r5 ; set slowmode of the _GetKeyState function
; Jump to _GetKeyState
mov.l GetKeyStateAddress,r0
jsr @r0 ;call _GetKeyState
nop
nop
; after _GetKeyState
; return to the calling position
add #4,r15
lds.l @r15+,pr
rts
nop
GetKeyStateAddress:
.data.l h'xxxxxxxx ; addres of the compatible _GetKeyState function
```
## IsKeyUp function from FxLib
This function doesn't works at all like IsKeyDown. This one use the syscall `0x24C` called "Chattering" by the fxLib.
The prototype seem to be :
```C++
int Chattering(unsigned char* coord);
```
with key coordinates in an array of two cells, the first is the cols, the seconds is the row of the key. This syscall return 1 if the key is pressed.
Syscall are writed in the OS code, so when there is an OS update, syscall are generally updated to works on the new hardware (it's the case of a lot of usefull syscall). But this one seem to not work on SH4 calc, it allays return 0. So maybe Casio choosed to disable this syscall.
Anyway, no matter the original implementation, we will also use `_GetKeyState` and just put a "not" at the end.
```asm
_IsKeyUpReplacement
; before this injection, there is call of _KeyCodeConvert that put an array of two byte that respectivly contain the col and the line in the stack
; So we put them into registers to use them letter in the _GetKeyState function that we will call
mov r15,r4 ; first param of the _GetKeyState function
mov #2,r5 ; set slowmode of the _GetKeyState function
; Jump to _GetKeyState
mov.l GetKeyStateAddress2,r0
jsr @r0 ;call _GetKeyState
nop
nop
; after _GetKeyState
; Negate _GetKeyState output
not r0,r0
and #1,r0
; return to the calling position
add #4,r15
lds.l @r15+,pr
rts
nop
GetKeyStateAddress2:
.data.l h'xxxxxxxx ; addres of the compatible _GetKeyState function
```
## KeyDown function
This is the same problem as the IsKeyDown function, the I/O register change so we can not read the keyboard input on the SH4 cpu. To identify this function, this was a little harder, because this function is not precompilated like the FxLib. So I found 2 binary implementation of this function in asm code:
* when the first line offset is equal to 0 modulo 4
* when it's equal to 2 modulo 4.
Some asm code work only when it's on a mod4=0, like
```
mov.l @(h'4,pc),r0
```
be cause it can read a longword (4byte) only at an offset mod4=0 and the number in parameter need to be divisible by 4. That explains the difference between the two implementations.
So we replace the KeyDown function with this code
```asm
_KeyDownReplacement ; put this at beginning of the KeyDown function. (the first four byte replaced : 2FE6634C)
;before : keycode in r4 ; keycode=col<<4 + row
sts.l pr,@-r15
mov.l r1,@-r15
mov.l r5,@-r15
; add #-2,r15 ;r15 need to always contain a number divisible by 4 (because when we put a longword of 4byte in the stack, we can only put it on adress multiple of 4)
;get the col
mov #-4,r0
mov r4,r1
shld r0,r1
;get the row
mov r4,r0
and #h'f,r0
; mov.b r0,@-r15
; mov.b r1,@-r15
;prepartion of the array content
shll8 r1
add r0,r1
shll16 r1
mov.l r1,@-r15
;prepare _GetKeyState call
mov r15,r4 ; get array address
mov #3,r5 ; set slowmode of _GetKeyState function
mov.l GetKeyStateAddress3,r0
jsr @r0 ;call _GetKeyState
nop
;after _GetKeyState
add #4,r15
mov.l @r15+,r5
mov.l @r15+,r1
lds.l @r15+,pr
rts
nop
GetKeyStateAddress3:
.data.l h'xxxxxxxx ; addres of the compatible _GetKeyState function
```
## monochromeLib and syscall call method
Monochromelib call OS syscall with this C++ code
```c++
static int SysCallCode[] = {0xD201422B,0x60F20000,0x80010070};
static int (*SysCall)( int R4, int R5, int R6, int R7, int FNo ) = (void*)&SysCallCode;
char* ML_vram_adress()
{
return (char*)((*SysCall)(0, 0, 0, 0, 309));
}
```
The array SysCallCode is writed in the memory at the address > `0x0810000`. Once this array is writen into this memory, it jump on it. The content of this array is a binary code to run syscall. That's mean that this array need to be stored in a memory that can be read, writen and executed. This was possible in SH3, but on the SH4 CPU, this memory cannot be executed.
An easy solution is to avoid writting this array on this memory by making it a "const". As const, it will stay in the programm "instruction list", and will not be copied anywhere. And the programm instruction list is obviously still readable and executable.
```C++
static const int SysCallCode[] = {0xD201422B,0x60F20000,0x80010070};
static int (*SysCall)( int R4, int R5, int R6, int R7, int FNo ) = (void*)&SysCallCode;
char* ML_vram_adress()
{
return (char*)((*SysCall)(0, 0, 0, 0, 309));
}
```
The code above is not a solution for us cause we cannot edit the C without sources.
Another problem is that there is many binary implementation for this solution, because it depends of the parameters. And it's too small to look it up like we do on `isKeyDown`.
Here is an assembly that can be generated from the C++ code
```asm
mov.l @(H'114,pc),r3 ; It get the address where the address of the array is written, here it's 0x08100014
mov.l @(H'10c,pc),r2 ; It get the syscall number from the parameter, here it's 0x135
mov.l @r3,r0 ; It get the address of the array, here it's 0x08100008
jsr @r0 ; Jump to the array
mov.l r2,@-r15 ; This is executed just before to jump : it put the syscall number in the stack
```
So to solve the problem, I put this code at the end of the file
```asm
_SyscallFix
mov.l #h'80010070,r2 ; the syscall table (where we jump to execute a syscall)
jmp @r2 ; Jump to the syscall table
mov.l @r15,r0 ; Just before to jump, put the value in the stack to the register r0 (the value in the stack is the syscall number)
```
And with our original code, i edit it a little :
```asm
mov.l @(H'114,pc),r3 ; I change the value pointed to be the address of my function SyscallFix (added at the end of the file)
mov.l @(H'10c,pc),r2
mov.l r3,r0 ; change to put the address get at the first line in r0
jsr @r0 ; Jump to the my added code
mov.l r2,@-r15
```
And it work well with this solution.
The hardest part is how we find the original implementation, because it changes everytime. All the line that are here everytime, but sometime separated by other instructions.

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2013-2019 Aurélien Labate <aurelien@labate.me>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

15
README.md Normal file
View File

@ -0,0 +1,15 @@
# SH4 Compatibility tool
**SH4 Compatibility tool** takes binaries (aka addins) for old SH3 Casio Calculators and make them compatible with newer sh4 calculators (power graphic 2).
This tool has originally been created by Ziqumu (aka Alabate) during summer of 2013 for [Planète Casio](https://www.planet-casio.com/Fr/forums/topic12292-1-SH4-compatibility-tool.html).
Documentation about problem solved by this tool can be found [here on DOCUMENTATION.md](./DOCUMENTATION.md)
## Thanks to
* [Casimo](https://www.planet-casio.com/Fr/compte/voir_profil.php?membre=Casimo) to have created the first keyboard input function compatible with SH4
* Simon Lothar and Andreas Bertheussen for their documentations on Casio syscalls
* [Kristaba](https://www.planet-casio.com/Fr/compte/voir_profil.php?membre=Kristaba) pour [ce topic](http://www.planet-casio.com/Fr/forums/lecture_sujet.php?id=9216&page=1)
* [Alphacreator](https://www.planet-casio.com/Fr/compte/voir_profil.php?membre=Alphacreator) and [Maliafo](https://www.planet-casio.com/Fr/compte/voir_profil.php?membre=Maliafo) to have tried programs generated by this tool
* Ayfer-Marie who gave me access to her SH4 calculator.

284
_getKeyState.asm Normal file
View File

@ -0,0 +1,284 @@
;param 1 (in r4) : Adress of an array of two unsigned char, with in the first cell the col, and in the second the row
;param 2 (in r5) : slowMode : this determine the time this function will wait to emulate the olds functions
; -n : number of loop
; 0 : fatest as possible
; 1 : = duration of IsKeyDown function
; 2 : = duration of IsKeyUp function
; 3 : = duration of KeyDown function
;return (in r0) 1 if the key is pressed.
_GetKeyState
;First put current register values in the stack
sts.l pr,@-r15
mov.l r1,@-r15
mov.l r2,@-r15
mov.l r3,@-r15
mov.l r6,@-r15
mov.l r7,@-r15
mov.l r8,@-r15
mov.l r9,@-r15
mov r4,r8 ; first param
mov r5,r9 ; second param
;check the os version with the syscall 0x0015 | if I use only 1 byte for chars, and 2 for short, it crash on all calc but not on emulator. But these type are valid because the syscall only edit the correct number of byte.
add #-4,r15 ; main version : unsigned char
mov r15,r4
add #-4,r15 ; minor version : unsigned char
mov r15,r5
add #-4,r15 ; release : unsigned short
mov r15,r6
add #-4,r15 ; build : unsigned short
mov r15,r7
;call syscall
mov.l #h'80010070,r2
jsr @r2
mov #h'15,r0
;put os version into r6
add #8,r15
mov.b @r15,r6 ; minor version
add #4,r15
mov.b @r15,r0 ; main version
add #4,r15
shll8 r0 ; r0 = r0<<8
add r0,r6
;reserved registers :
;r9 second param
;r6 OS version
; read and checks coords
mov.b @r8,r7 ; r7 = Key's column
mov.b @(1,r8),r0
mov r0,r8 ; r8 = Key's row
;verify the row value : 0 ≤ row ≤ 9
mov #0, r0
cmp/gt r8,r0 ; if r0 > r8 ⇒ if 0 > row
bt NegativeEnd
mov #9,r1
cmp/gt r1,r8 ; if r8 > r1 ⇒ if row > 9
bt NegativeEnd
;verify the column value : 0 ≤ row ≤ 6
cmp/gt r7,r0 ; if r0 > r7 ⇒ if 0 > column
bt NegativeEnd
mov #6,r1
cmp/gt r1,r7 ; if r7 > r1 ⇒ if column > 6
bt NegativeEnd
;check if os is > 2.02
mov.w #h'0202,r0
cmp/ge r0,r6 ; r0 ≤ r6
bt SH4
;reserved registers :
;r9 second param
;r8 Key's row
;r7 Key's col
;SH3 part
;r6 = smask = 0x0003 << (( row %8)*2);
mov r8,r0 ; row->r0
and #7,r0 ; %8
add r0,r0 ; *2
mov #3,r6
shld r0,r6 ; 3<<
;r5 = cmask = ~( 1 << ( row %8) );
mov r8,r0 ; row->r0
and #7,r0 ; %8
mov #1,r5
shld r0,r5 ; 1<<
not r5,r5 ; ~
;reserved registers :
;r9 second param
;r8 Key's row
;r7 Key's col
;r6 smask
;r5 cmask
;Preparation of the gbr register
mov.l #h'A4000100,r0
ldc r0,gbr
;RowCond : if(row <8)
mov #8,r0
cmp/gt r8,r0 ; if r0>r8 ; row>=8
bf rowCond_Else
;rowCond_begin
;*PORTB_CTRL = 0xAAAA ^ smask;
mov r6,r0
mov.w #h'AAAA,r1
xor r1,r0
mov.w r0,@(h'02,gbr)
;*PORTM_CTRL = (*PORTM_CTRL & 0xFF00 ) | 0x00AA;
mov.w @(h'18,gbr),r0 ; *PORTM_CTRL->r0
mov.w #h'FF00,r1
and r1,r0 ; *PORTM_CTRL & 0xFF00
or #h'AA,r0 ; | 0x00AA;
mov.w r0,@(h'18,gbr)
;delay()
bsr delay
mov #-10,r4
;*PORTB = cmask;
mov r5,r0
mov.b r0,@(h'22,gbr) ;PORTB = cmask
;*PORTM = (*PORTM & 0xF0 ) | 0x0F;
mov.b @(h'38,gbr),r0 ; *PORTM->r0
and #h'F0,r0 ; *PORTM & 0xF0
or #h'0F,r0 ; | 0x0F;
mov.b r0,@(h'38,gbr)
bra rowCond_End
nop
rowCond_Else:
; *PORTB_CTRL = 0xAAAA;
mov.w #h'AAAA,r0
mov.w r0,@(h'02,gbr)
; *PORTM_CTRL = ((*PORTM_CTRL & 0xFF00 ) | 0x00AA) ^ smask;
mov.w @(h'18,gbr),r0
mov.w #h'FF00,r1
and r1,r0 ; *PORTM_CTRL & 0xFF00
or #h'AA,r0 ; | 0x00AA;
xor r6,r0 ; ^ smask;
mov.b r0,@(h'18,gbr)
;delay()
bsr delay
mov #-10,r4 ;In the begin this was 5, but as the delay function is faster, i need to put more
;*PORTB = 0xFF;
mov.b #h'ff,r0
mov.b r0,@(h'22,gbr) ;PORTB = 0xFF
;*PORTM = (*PORTM & 0xF0 ) | cmask;
mov.b @(h'38,gbr),r0
and #h'F0,r0 ; *PORTM & 0xF0
or r5,r0 ; | cmask;
mov.b r0,@(h'38,gbr)
rowCond_End:
;reserved registers :
;r9 second param
;r8 Key's row
;r7 Key's col
;delay()
bsr delay
mov #-10,r4
;result = (~(*PORTA))>>column & 1;
mov.b @(h'20,gbr),r0
not r0,r6 ; r6 = ~r0
neg r7,r0 ; r0 = -column
shld r0,r6 ; r6 = r6>>column
mov.b #1,r0
and r0,r6
;reserved registers :
;r9 second param
;r8 Key's row
;r7 Key's col
;r6 result
;delay()
bsr delay
mov #-10,r4
; *PORTB_CTRL = 0xAAAA;
mov.w #h'AAAA,r0
mov.w r0,@(h'02,gbr)
;*PORTM_CTRL = (*PORTM_CTRL & 0xFF00 ) | 0x00AA;
mov.w @(h'18,gbr),r0
mov.w #h'FF00,r1
and r1,r0 ; *PORTM_CTRL & 0xFF00
or #h'AA,r0 ; | 0x00AA;
mov.w r0,@(h'18,gbr)
;delay()
bsr delay
mov #-10,r4
; *PORTB_CTRL = 0x5555;
mov.w #h'5555,r0
mov.w r0,@(h'02,gbr)
;*PORTM_CTRL = (*PORTM_CTRL & 0xFF00 ) | 0x0055;
mov.w @(h'18,gbr),r0
mov.w #h'FF00,r1
and r1,r0 ; *PORTM_CTRL & 0xFF00
or #h'55,r0 ; | 0x0055;
mov.w r0,@(h'18,gbr)
;delay()
bsr delay
mov #-10,r4
;End of SH3 part
bra AllEnd
nop
SH4:
;Add 3 to the second param (if >0)to select the right wait time
mov #0,r0
cmp/gt r0,r9
bf negatif2ndParam
add #3,r9
negatif2ndParam:
;get the main keyboard regsiter address+1
mov.l #H'A44B0001,r1
mov r8,r0
tst #1,r0 ;if row is even T=1 else T=0
add r8,r1
bt row_even ; Jump if T=1
add #-2,r1
row_even:
mov.b @r1,r0 ; The byte that contain the row data is now in R0
mov #1,r1
shld r7,r1 ; R9 now contain 1<<col
tst r1,r0 ; if key is pressed T=0
movt r0
not r0,r0
and #h'1,r0
mov r0,r6
bra AllEnd
nop
NegativeEnd:
mov #0,r6
;reserved registers :
;r9 second param
;r8 Key's row
;r7 Key's col
;r6 result
AllEnd:
;Wait the correct time to emulate old functions
bsr delay
mov r9,r4
;put result to return register : r0
mov r6,r0
;take out data from stack
mov.l @r15+,r9
mov.l @r15+,r8
mov.l @r15+,r7
mov.l @r15+,r6
mov.l @r15+,r3
mov.l @r15+,r2
mov.l @r15+,r1
lds.l @r15+,pr
rts
nop
; delay : Wait a defined time
;param 1 (in r4) : slowMode : this determine the time this function will wait to emulate the olds functions
; -n : number of loop
; 0 : fatest as possible (equivalent to -1)
; 1 : = duration of IsKeyDown function for SH3
; 2 : = duration of IsKeyUp function for SH3
; 3 : = duration of KeyDown function for SH3
; 4 : = duration of IsKeyDown function for SH4
; 5 : = duration of IsKeyUp function for SH4
; 6 : = duration of KeyDown function for SH4
delay:
;if r4<=0 then it's the number of loop
mov #0,r0
cmp/ge r0,r4
bf LoopNumber
;Search the number of loop needed
add r4,r4 ; *2
mova loopNumbersList,r0
add r4,r4 ; *2 because there is 4 byte per number of loop (this method take less space than use "MUL.L")
add r4,r0
mov.l @r0,r1
bra target_loopBegin
nop ; this nop is added because without the loopNumbersList is not divisible per 4
loopNumbersList:
.data.l h'00000001 ;fastest (can't be under 1)
.data.l h'000006F7 ;IsKeyDown SH3
.data.l h'00012D1F ;IsKeyUp SH3
.data.l h'00000001 ;KeyDown SH3
.data.l h'000005CD ;IsKeyDown SH4
.data.l h'0000F1A8 ;IsKeyUp SH4
.data.l h'00000001 ;KeyDown SH4
LoopNumber:
neg r4,r1
;Begin : r1 contain the number of loop
target_loopBegin:
dt r1 ; decrement and test if(r1==0)
bf target_loopBegin
rts
nop

62
index.php Normal file
View File

@ -0,0 +1,62 @@
<?php
session_start();
?>
<!doctype html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>SH4 Compatibility Tool</title>
<link rel="canonical" href="http://wiki.planet-casio.com/tools/SH4compatibility"/>
<link rel="stylesheet" type="text/css" href="system/style.css?100313" />
<script type="text/javascript">//Google analytics
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-39078814-1']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
</head>
<body>
<div id="headerBar">
<a class="Cell CellL" href="http://wiki.planet-casio.com/tools/SH4compatibility">
<h1>SH4 Compatibility Tool</h1>
</a>
<a class="Cell CellR casiopeiaLink" href="http://www.casiopeia.net/forum/">
<strong>Casiopeia</strong>
</a>
<a class="Cell CellR PClink" href="http://www.planet-casio.com/Fr/">
<strong>Planète Casio</strong>
</a>
<a class="Cell CellR" href="http://wiki.planet-casio.com/">
<strong>Casio Universal Wiki</strong>
</a>
<?php
if(!empty($_SESSION['msg_text']) && !empty($_SESSION['msg_color']))
{
echo '<div style="color:'.$_SESSION['msg_color'].'">'.$_SESSION['msg_text'].'</div>';
$_SESSION['msg_text']='';
}
?>
</div>
<div id="loadBox" style="display:block;font-size:14px;">
<form method="post" action="system/converter.php" enctype="multipart/form-data">
<h4 style="font-size:16px;">Make your g1a compatible for casio SH4 (Power Graphic 2)</h4>
<input type="file" name="file" accept=".g1a"/><br/>
<label><input type="checkbox" name="slow" checked="checked"/> Slow down modified function to simulate the old calculators (recommended)</label><br/>
<input type="submit"/><br/>
<span style="color:red;font-weight:bold;">Warning! This tool can cause damage on your calculator! Use it at your own risk. Planet-Casio, Casiopeia and me will not be responsible for any damage.</span>
</form>
</div>
<div id="footer">
<i>Le compatibidule, et vos add-ins pullulent !</i><br />
Developed by Ziqumu for <a href="http://www.planet-casio.com/Fr/">Planete Casio</a> and <a href='http://www.casiopeia.net/'>Casiopeia</a>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
</body>
</html>

278
system/converter.php Normal file
View File

@ -0,0 +1,278 @@
<?php
session_start();
$version = '312E3031'; //1.01
ini_set("display_errors",0);
if(!empty($_FILES['file']['name']))
{
// echo $_FILES['file']['name'].'<br/>';
// require_once('functions.php');
//Get the g1a
$InFile = file_get_contents($_FILES['file']['tmp_name']);
$InFileSize = strlen($InFile);
$InFile = bin2hex($InFile);
//Check if the file is a g1a
if(strtolower($InFile[16].$InFile[17]) != '0c')
{
$_SESSION['msg_text']='The file is not recognized as a .g1a';
$_SESSION['msg_color']='red';
header("Location: ../");
exit;
}
//Check if the is not already edited by this tool
if(strtolower(substr($InFile,-60,52)) == '000053483420436f6d7061746962696c69747920546f6f6c2076')
{
$_SESSION['msg_text']='This file has already been modified by this tool. (version '.hex2bin(substr($InFile,-8)).')';
$_SESSION['msg_color']='red';
header("Location: ../");
exit;
}
//Verify if the file length is a divisible by 4 (cause the code that i add to the end is done only for this)
if($InFileSize%4 != 0)
{
$OutFile = str_pad($InFile, ($InFileSize+4-($InFileSize%4))*2, '0', STR_PAD_RIGHT);
$InFileSize = $InFileSize+4-($InFileSize%4);
}
else
$OutFile = $InFile;
//Input functions fix
$inputFix = false;
$GetKeyStateAdress = str_pad(dechex($InFileSize+0x300000), 8, '0', STR_PAD_LEFT);
//IsKeyDown
$isKeyDownCode = '4f227ffc63f3bee52f367f0463f0633c43118b0384f1600c401189037f044f26000be000bf5664f37f044f26000b0009';
$isKeyDownPosition = strpos($OutFile,$isKeyDownCode);
if($isKeyDownPosition !== false)
{
//Test if there is more than one function
if(strpos($OutFile,$isKeyDownCode,$isKeyDownPosition+1) !== false)
{
$_SESSION['msg_text']='Error : we found more than one IsKeyDown function !';
$_SESSION['msg_color']='red';
header("Location: ../");
exit;
}
//Replacement
$inputFix = true;
if(!isset($_POST['slow']))$slowcode = '00'; //fastmode
else $slowcode = '01'; //IsKeyDown Slowmode
$OutFile = substr($OutFile,0,$isKeyDownPosition+(0xc*2)) . '64F3E5'.$slowcode.'D003400B000900097F044F26000B0009' . $GetKeyStateAdress . substr($OutFile,$isKeyDownPosition+(0xc+20+4)*2);
}
// echo (strlen($OutFile)/2).'->0 <br/> ';
//IsKeyUp
$isKeyUpCode = '4f227ffc63f3becd2f367f0463f0633c43118b0384f1600c401189037f044f26000be000d208420b64f3200800297f044f26000b00095555a40001000000aaaa0000ff00a4000120';
$isKeyUpPosition = strpos($OutFile,$isKeyUpCode);
if($isKeyUpPosition !== false)
{
//Test if there is more than one function
if(strpos($OutFile,$isKeyUpCode,$isKeyUpPosition+1) !== false)
{
$_SESSION['msg_text']='Error : we found more than one IsKeyUp function !';
$_SESSION['msg_color']='red';
header("Location: ../");
exit;
}
//Replacement
$inputFix = true;
if(!isset($_POST['slow']))$slowcode = '00'; //fastmode
else $slowcode = '02'; //IsKeyUp Slowmode
$OutFile = substr($OutFile,0,$isKeyUpPosition+(0xc*2)) . '64F3E5'.$slowcode.'D004400B000900096007C9017F044F26000B0009' . $GetKeyStateAdress . substr($OutFile,$isKeyUpPosition+(0xc+24+4)*2);
}
// echo (strlen($OutFile)/2).'->1 <br/> ';
//KeyDown : pc%4=0
$keydownfix=false;
$KeyDownCode = '2fe6634c2fd6e50f2fc643092fb625492fa643092f96665c4f2260637ffc40112f308b01a005c90760077001c907600770016403';
$KeyDownPosition = strpos($OutFile,$KeyDownCode);
if($KeyDownPosition !== false)
{
//Test if there is more than one function
if(strpos($OutFile,$KeyDownCode,$KeyDownPosition+1) !== false)
{
$_SESSION['msg_text']='Error 1 : we found more than one KeyDown function !';
$_SESSION['msg_color']='red';
header("Location: ../");
exit;
}
$inputFix = true;
$keydownfix=true;
}
//KeyDown : pc%4=2
$KeyDown2Code = '634c2fe6e50f2fd643092fc625492fb643092fa6665c2f9660634f2240117ffc2f308b01a005c90760077001c90760077001';
$KeyDown2Position = strpos($OutFile,$KeyDown2Code);
if($KeyDown2Position !== false)
{
//Test if there is more than one function
if(strpos($OutFile,$KeyDown2Code,$KeyDown2Position+1) !== false || $keydownfix)
{
$_SESSION['msg_text']='Error 2 : we found more than one KeyDown function !';
$_SESSION['msg_color']='red';
header("Location: ../");
exit;
}
$inputFix = true;
$keydownfix=true;
//Decallage pour avoir pc%4=0
$OutFile = substr($OutFile,0,$KeyDown2Position) . '0009'. substr($OutFile,$KeyDown2Position+4);
$KeyDownPosition = $KeyDown2Position+4;
}
//KeyDown replacement
if($keydownfix)
{
if(!isset($_POST['slow']))$slowcode = '00'; //fastmode
else $slowcode = '03'; //IsKeyDown Slowmode
$OutFile = substr($OutFile,0,$KeyDownPosition) . '4F222F162F56E0FC6143410D6043C90F4118310C41282F1664F3E5'.$slowcode.'D004400B00097F0465F661F64F26000B00090009' . $GetKeyStateAdress . substr($OutFile,$KeyDownPosition+54+2+40+8);
}
//Part to add at the end for IsKeyDown, IsKeyUp, and KeyDown
if($inputFix)
{
$delayLoop = 'F6'; //-10
$OutFile .='4F222F162F262F362F662F762F862F96684369537FFC64F37FFC65F37FFC66F37FFC67F3D21F420BE0157F0866f07F0460f07F044018360C678084816803E0003087897BE1093817897830778976E106371789739023360389596083C907300CE603460D6083C907E501450D6557D00E401EE00830878B1960639111201AC101C50C910E2019CBAAC10CB065E4'.$delayLoop.'6053C022C438C9F0CB0FC038A01700090202AAAAFF0080010070A4000100902CC101C50C912A2019CBAA206AC10CB04CE4'.$delayLoop.'E0FFC022C438C9F0205BC038B044E4'.$delayLoop.'C4206607607B460DE0012609B03CE4'.$delayLoop.'9012C101C50C91102019CBAAC10CB033E4'.$delayLoop.'900BC101C50C91072019CB55C10CB02AE4'.$delayLoop.'A01B0009AAAAFF005555E00039078B007903D1086083C801318C890071FE6010E101417D201800296007C9016603A00400090000A44B0001E600B00B6493606369F668F667F666F663F662F661F64F26000B0009E00034038B14344CC702344C304C6102A00F0009'.
// Number of loop to emulate old functions
'00000001'. //fastest (can't be under 1)
'000006F7'. //IsKeyDown SH3
'00012D1F'. //IsKeyUp SH3
'00000001'. //KeyDown SH3
'000005CD'. //IsKeyDown SH4
'0000F1A8'. //IsKeyUp SH4
'00000001'. //KeyDown SH4
'614B41108BFD000B00090009';
}
//Syscall bug fix
$position = 0;
$SyscallFix = false;
$syscallCodeAddress = str_pad(dechex((strlen($OutFile)/2)+0x300000), 8, '0', STR_PAD_LEFT);
while(isset($OutFile[$position+3]))
{
if($OutFile[$position]=='4' && strtolower($OutFile[$position+1])=='f' && $OutFile[$position+2]=='2' &&$OutFile[$position+3]=='2')
{
//The instruction of the actual line is 4F22: sts.l pr,@-15
//The $position+4 or $position+8 instruction is d3xx:mov.l @(h'xx,pc),r3 // and @(h'xx,pc) contain the an address >= 0x08100000 (and we will suppose < 0x08200000)
$pc=0;
$valide = true;
if(strtolower($OutFile[$position+4]=='d') && $OutFile[$position+5]=='3')
{
$disp = ord(hex2bin($OutFile[$position+6].$OutFile[$position+7]));
$pc = ($position/2)+4;
$subOffset = 4;
}
elseif(strtolower($OutFile[$position+8]=='d') && $OutFile[$position+9]=='3')
{
$disp = ord(hex2bin($OutFile[$position+10].$OutFile[$position+11]));
$pc = ($position/2)+4+4;
$subOffset = 8;
}
else
$valide=false;
//Check if the address >= 0x08100000 and <0x08200000
if($valide)
{
$ToChange_Address = (($pc&0xFFFFFFFC)+($disp<<2))*2;
if(substr($OutFile,$ToChange_Address,3) != '081')
$valide=false;
}
//Search the row 92XX: mov.w @(h'xx,pc),r2 // This is the instruction that get the syscall number, so if I want it, I can. (just do as above)
if($valide)
{
$valide=false;
for($i=0;$i<=4;$i++)
{
if($OutFile[$position+$subOffset+($i*4)] == 'd' && $OutFile[$position+$subOffset+($i*4)+1] == '3')
{
$subOffset+=$i*4;
$valide = true;
break;
}
}
}
//Search the row 6032: mov @R3,r0 // I need to edit this line to : mov r3,r0
if($valide)
{
$valide=false;
for($i=0;$i<=4;$i++)
{
if(substr($OutFile,$position+$subOffset+($i*4),4) == '6032')
{
$ToChange_Instruction = $position+$subOffset+($i*4);
$subOffset+=$i*4;
$valide = true;
break;
}
}
}
//Search the row 400b: jsr @r0 //jump instruction
if($valide)
{
$valide=false;
for($i=0;$i<=4;$i++)
{
if(substr($OutFile,$position+$subOffset+($i*4),4) == '400b')
{
$subOffset+=$i*4;
$valide = true;
break;
}
}
}
//Fixing
if($valide)
{
$SyscallFix = true;
//Change the mov instruction
$OutFile = substr($OutFile,0,$ToChange_Instruction) . '6033' . substr($OutFile,$ToChange_Instruction+4);
//Change the value moved (to the address of the code, to let him jump to it)
$OutFile = substr($OutFile,0,$ToChange_Address) . $syscallCodeAddress . substr($OutFile,$ToChange_Address+8);
}
}
$position+=4;
}
//Add the code for syscall if needed
if($SyscallFix)
{
$OutFile .= 'D201422B60F2000080010070';
}
//Add the version mark
$OutFile .= '0000000053483420436F6D7061746962696C69747920546F6F6C2076'.$version; //SH4 Compatibility Tool v$version
//Recalcul header
$OutFileSize = strlen($OutFile)/2;
if($InFileSize != $OutFileSize)
{
//OFFSET 0xE : last filesize byte + 65(0x41) and NOT (inverted)
$offset0xE = $OutFileSize + 0x41;
$offset0xE = 0xFFFFFFFF - $offset0xE;
$offset0xE = dechex($offset0xE);
$offset0xE = str_pad($offset0xE, 2, '0', STR_PAD_LEFT);
$offset0xE = substr($offset0xE, -2); // take the last byte
//OFFSET 0x10 : filesize NOT (inverted)
$offset0x10 = 0xFFFFFFFF - $OutFileSize;
$offset0x10 = dechex($offset0x10);// to hex
$offset0x10 = str_pad($offset0x10, 8, '0', STR_PAD_LEFT);
//OFFSET 0x14 : last filesize byte + 184(0xB8) and NOT (inverted)
$offset0x14 = $OutFileSize + 0xB8;
$offset0x14 = 0xFFFFFFFF - $offset0x14;
$offset0x14 = dechex($offset0x14);
$offset0x14 = str_pad($offset0x14, 2, '0', STR_PAD_LEFT);
$offset0x14 = substr($offset0x14, -2);
//OFFSET 0x1f0 : filesize
$offset0x1f0 = $OutFileSize;
$offset0x1f0 = dechex($offset0x1f0);// to hex
$offset0x1f0 = str_pad($offset0x1f0, 8, '0', STR_PAD_LEFT);
//Modifs
$OutFile = substr($OutFile,0,0xE*2). $offset0xE . substr($OutFile,0xf*2,2).
$offset0x10 . substr($OutFile,0x14*2,0x1dc*2) . $offset0x1f0 . substr($OutFile,0x1f4*2);
// echo $InFileSize . ' - ' .$OutFileSize;exit;
}
//making
$fileContent = pack('H*',$OutFile);
//sending
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment;filename="'.substr($_FILES['file']['name'],0,-4).'.g1a"');
header('Cache-Control: max-age=0');
$fh = fopen('php://output', 'wb');
fwrite($fh, $fileContent);
fclose($fh);
}
else
{
header("Location: ../");
}
?>

188
system/functions.php Normal file
View File

@ -0,0 +1,188 @@
<?php
$Fxi2RawArray = array(
255 => "18", 254 => "19", 253 => "1a", 252 => "1b", 251 => "1c",
250 => "1d", 249 => "1e", 248 => "1f", 247 => "10", 246 => "11",
245 => "12", 244 => "13", 243 => "14", 242 => "15", 241 => "16",
240 => "17", 239 => "08", 238 => "09", 237 => "0a", 236 => "0b",
235 => "0c", 234 => "0d", 233 => "0e", 232 => "0f", 231 => "00",
230 => "01", 229 => "02", 228 => "03", 227 => "04", 226 => "05",
225 => "06", 224 => "07", 223 => "38", 222 => "39", 221 => "3a",
220 => "3b", 219 => "3c", 218 => "3d", 217 => "3e", 216 => "3f",
215 => "30", 214 => "31", 213 => "32", 212 => "33", 211 => "34",
210 => "35", 209 => "36", 208 => "37", 207 => "28", 206 => "29",
205 => "2a", 204 => "2b", 203 => "2c", 202 => "2d", 201 => "2e",
200 => "2f", 199 => "20", 198 => "21", 197 => "22", 196 => "23",
195 => "24", 194 => "25", 193 => "26", 192 => "27", 191 => "58",
190 => "59", 189 => "5a", 188 => "5b", 187 => "5c", 186 => "5d",
185 => "5e", 184 => "5f", 183 => "50", 182 => "51", 181 => "52",
180 => "53", 179 => "54", 178 => "55", 177 => "56", 176 => "57",
175 => "48", 174 => "49", 173 => "4a", 172 => "4b", 171 => "4c",
170 => "4d", 169 => "4e", 168 => "4f", 167 => "40", 166 => "41",
165 => "42", 164 => "43", 163 => "44", 162 => "45", 161 => "46",
160 => "47", 159 => "78", 158 => "79", 157 => "7a", 156 => "7b",
155 => "7c", 154 => "7d", 153 => "7e", 152 => "7f", 151 => "70",
150 => "71", 149 => "72", 148 => "73", 147 => "74", 146 => "75",
145 => "76", 144 => "77", 143 => "68", 142 => "69", 141 => "6a",
140 => "6b", 139 => "6c", 138 => "6d", 137 => "6e", 136 => "6f",
135 => "60", 134 => "61", 133 => "62", 132 => "63", 131 => "64",
130 => "65", 129 => "66", 128 => "67", 127 => "98", 126 => "99",
125 => "9a", 124 => "9b", 123 => "9c", 122 => "9d", 121 => "9e",
120 => "9f", 119 => "90", 118 => "91", 117 => "92", 116 => "93",
115 => "94", 114 => "95", 113 => "96", 112 => "97", 111 => "88",
110 => "89", 109 => "8a", 108 => "8b", 107 => "8c", 106 => "8d",
105 => "8e", 104 => "8f", 103 => "80", 102 => "81", 101 => "82",
100 => "83", 99 => "84", 98 => "85", 97 => "86", 96 => "87",
95 => "b8", 94 => "b9", 93 => "ba", 92 => "bb", 91 => "bc",
90 => "bd", 89 => "be", 88 => "bf", 87 => "b0", 86 => "b1",
85 => "b2", 84 => "b3", 83 => "b4", 82 => "b5", 81 => "b6",
80 => "b7", 79 => "a8", 78 => "a9", 77 => "aa", 76 => "ab",
75 => "ac", 74 => "ad", 73 => "ae", 72 => "af", 71 => "a0",
70 => "a1", 69 => "a2", 68 => "a3", 67 => "a4", 66 => "a5",
65 => "a6", 64 => "a7", 63 => "d8", 62 => "d9", 61 => "da",
60 => "db", 59 => "dc", 58 => "dd", 57 => "de", 56 => "df",
55 => "d0", 54 => "d1", 53 => "d2", 52 => "d3", 51 => "d4",
50 => "d5", 49 => "d6", 48 => "d7", 47 => "c8", 46 => "c9",
45 => "ca", 44 => "cb", 43 => "cc", 42 => "cd", 41 => "ce",
40 => "cf", 39 => "c0", 38 => "c1", 37 => "c2", 36 => "c3",
35 => "c4", 34 => "c5", 33 => "c6", 32 => "c7", 31 => "f8",
30 => "f9", 29 => "fa", 28 => "fb", 27 => "fc", 26 => "fd",
25 => "fe", 24 => "ff", 23 => "f0", 22 => "f1", 21 => "f2",
20 => "f3", 19 => "f4", 18 => "f5", 17 => "f6", 16 => "f7",
15 => "e8", 14 => "e9", 13 => "ea", 12 => "eb", 11 => "ec",
10 => "ed", 9 => "ee", 8 => "ef", 7 => "e0", 6 => "e1",
5 => "e2", 4 => "e3", 3 => "e4", 2 => "e5", 1 => "e6",
0 => "e7",
);
function fxiDecode($text)
{
global $Fxi2RawArray;
$fileSize = strlen($text);
$Return="";
for($i = 0 ; $i < $fileSize; $i++)
{
if(isset($Fxi2RawArray[ord($text[$i])]))
{
$Return.= $Fxi2RawArray[ord($text[$i])];
}
else
{
echo "Erreur : caractere non reconnu (erreur impossible)";exit;
}
}
return $Return;
}
function getFxiPic($text) //transform fxi pic an array of 4 array reprent all 4 colors. array of colors are coded like a g1m sheet (natural organisation : one bit represente one pixel) ; color order : Blue, orange, green, white
{
$textSize = strlen($text);
$actuelColor = 0;
$x = 15;
$y = 63;
for($i = 8 ; $i < $textSize; $i+=2)
{
//changement de couleur
if($i >=($actuelColor*2056+2056) )
{
$actuelColor++;
$i+=8;
$x = 15;
$y = 63;
}
//changement de colonne
if($y<0)
{
$y=63;
$x--;
}
//enregistrement de la case
$Return[$actuelColor][$y*16+$x] = hexdec($text[$i].$text[$i+1]);
$y--;
}
return $Return;
}
function getG1mPic($text) //transform G1m pic an array of 4 array reprent all 2 colors. array of colors are coded like a g1m sheet (natural organisation : one bit represente one pixel) ; color order : Blue, orange, green, white
{
$textSize = strlen($text);
$actuelColor = 0;
$i = 0;
while($actuelColor < 2 && $actuelColor*2048+$i<$textSize)
{
if($i >= 2048 )
{
$i = 0;
$actuelColor++;
}
$Return[$actuelColor][$i/2] = hexdec($text[$actuelColor*2048+$i].$text[$actuelColor*2048+$i+1]);
$i+=2;
}
// print_r($Return);exit;
return $Return;
}
//color=array(array(255,0,0),array(0, 0, 255),array(0, 128, 0),array(66, 174, 9))
function imageResizeAlpha($src, $coef)
{
$w = imagesx($src)*$coef;
$h = imagesy($src)*$coef;
$temp = imagecreatetruecolor($w, $h);
imagealphablending($temp, false);
imagesavealpha($temp, true);
$trans_layer_overlay = imagecolorallocatealpha($temp, 0, 0, 0, 127);
imagefill($temp, 0, 0, $trans_layer_overlay);
imagecopyresized($temp, $src, 0, 0, 0, 0, $w, $h, imagesx($src), imagesy($src));
return $temp;
}
//=array(array(255,0,0),array(0, 0, 255),array(0, 128, 0),array(66, 174, 9))
// $actualPic = 1;
//0 = rouge
//1 = bleu
//2 = vert
//3 = white
function writePics($pics, $colors, $size=3)
{
$nbrPics = count($pics);
header("Content-type: image/png");
$image = imagecreate(128,64);
$fond = imagecolorallocate($image, 255,255,255);
imagecolortransparent($image,$fond);
for($actualPic = 0 ; $actualPic < $nbrPics; $actualPic++)
{
// echo $actualPic;
$AColor = imagecolorallocate($image, $colors[$actualPic][0], $colors[$actualPic][1], $colors[$actualPic][2]);
for($xy = 0 ; $xy < 1024; $xy++)
{
$y = (int)($xy/16);
$x = ($xy%16)*8;
if($pics[$actualPic][$xy]&1)//00000001
ImageSetPixel($image, $x+7, $y, $AColor);
if(($pics[$actualPic][$xy]&2)>>1)//00000010
ImageSetPixel($image, $x+6, $y, $AColor);
if(($pics[$actualPic][$xy]&4)>>2)//00000100
ImageSetPixel($image, $x+5, $y, $AColor);
if(($pics[$actualPic][$xy]&8)>>3)//00001000
ImageSetPixel($image, $x+4, $y, $AColor);
if(($pics[$actualPic][$xy]&16)>>4)//00010000
ImageSetPixel($image, $x+3, $y, $AColor);
if(($pics[$actualPic][$xy]&32)>>5)//00100000
ImageSetPixel($image, $x+2, $y, $AColor);
if(($pics[$actualPic][$xy]&64)>>6)//01000000
ImageSetPixel($image, $x+1, $y, $AColor);
if(($pics[$actualPic][$xy]&128)>>7)//10000000
ImageSetPixel($image, $x, $y, $AColor);
}
}
//resizing
if($size >=2)
{
$image = imageResizeAlpha($image,$size);
}
//senging
imagepng($image);
}
?>

47
system/style.css Normal file
View File

@ -0,0 +1,47 @@
body{margin:0;padding:0;background-color:#f6f6f6;font-family:sans-serif;min-width:950px;}
/* header bar : title and links */
#headerBar{text-align:center;background-color:#ffffff;border:solid #a7d7f9 1px;width:95%;margin:15px auto;height:40px; box-shadow: 2px 2px 15px #d0d0d0;}
#headerBar div{margin:0;padding:0;color:red;}
#headerBar .Cell{height:40px;display:inline-block}
#headerBar .CellR{border-left:solid #a7d7f9 1px;float:right;}
#headerBar .CellL{border-right:solid #a7d7f9 1px;float:left;}
#headerBar a{color:#000;text-decoration:none;}
#headerBar a:hover{background-color:#ecfbfe;color:#120447; }
#headerBar .PClink:hover{color:#be1717;background-color:#fce9e9;border:solid #be1717 1px;margin:-1px -1px 0 0;}
#headerBar .casiopeiaLink:hover{color:#4283c6;background-color:#e2eef6;border:solid #4283c6 1px;margin:-1px -1px 0 0;}
#headerBar h1{font-weight:normal;font-family:sans-serif;font-size:26px;margin:5px 10px 0 10px;}
#headerBar strong{display:inline-block;font-weight:normal;font-size:18px;margin:9px 10px 0 10px;}
/* loadbox to get file to load */
#loadBox{display:block;width:40%;margin: 0 auto;padding:10px;box-shadow: 2px 2px 15px #d0d0d0;background-color:white;border:solid #a7d7f9 1px;text-align:center;}
#loadBox a{text-decoration:none;color:#0d90d4;margin-top:5px;display:inline-block;}
#loadBox h4{margin:0;padding:0;}
#loadBox a:hover{text-decoration:underline;}
/* errorBox error msg box */
#errorBox{overflow:hidden;display:none;width:90%;margin: 30px auto;padding:10px;box-shadow: 2px 2px 15px #d0d0d0;background-color:white;border:solid #a7d7f9 1px;text-align:center;}
#errorBox h3{margin:0;padding:0;font-size:1.2em;color:red;font-weight:normal;}
#errorBox a{text-decoration:none;color:#0d90d4;margin-top:5px;display:block;}
#errorBox a:hover{text-decoration:underline;}
/* settings Bar : name format and eventual password */
#settingsBar{width:95%;height:25px;margin:auto;text-align:center;margin-bottom:15px;}
#settingsBar .title{float:left;}
#settingsBar .format{float:right;}
#settingsBar .centralInput{display:none;}
/* #eactmaker the textarea and menu in top and in bottom */
#eactmaker{width:95%;margin: 0 auto;padding:0;box-shadow: 2px 2px 15px #d0d0d0;}
#eactmaker .buttons{background-color:white;height:33px;width:100%;border:solid #a7d7f9 1px;margin:0 auto;padding:0;}
#eactmaker .buttons.top{display:none;}
#eactmaker .buttons a{float:left;border-right:solid 1px #dddddd;margin:0;height:26px;padding:7px 15px 0 15px;text-decoration:none;color:#7b7b7b;display:none;}
#eactmaker .buttons a.img{padding:0 10px;height:33px;}/*comportement of the link for img */
#eactmaker .buttons a.img div{display:inline-block;height:33px;}
#eactmaker .buttons a:hover{color:#0d90d4;background-color:#ecfbfe;}
#eactmaker .buttons .right{border-left:1px solid #dddddd;border-right:0;float:right;}
#eactmaker .buttons input{border-style:solid;border-width:0 1px 0 1;background:none;display:inline-block;margin:0;height:33px;padding:0px 15px 0 15px;color:#7b7b7b;font-size:1.0em;font-weight:bold;cursor:pointer;}
#eactmaker .buttons input:hover{color:#0d90d4;background-color:#ecfbfe;}
#eactmaker #charsTab{background-color:#f3fdfe;width:100%;border:solid #a7d7f9 1px;margin:-1px auto 0 auto;padding:0;display:none;}
#eactmaker #charsTab div{margin:10px auto;position:relative;}
#eactmaker #charsTab a{position:absolute;display:block;width:23px;height:29px;}
#eactmaker #charsTab a:hover{border:solid #a7d7f9 1px;}
#eactmaker textarea{vertical-align: middle;width:100%;font-size:1.3em;border:solid #a7d7f9 1px;padding:0;max-width:100%;min-width:100%;min-height:30px;margin:-1px 0 -1px 0;}
#footer{font-size:10px;color:grey;float:right;width:100%;text-align:center;margin-top:15px;}
#footer a{color:grey;}
#footer a:hover{text-decoration:none;}