The binary is not a single monolithic block. I figured out the structure but it took a few false starts.
First I loaded the Binary into IDA starting from offset 0x200 as you hinted. The functions were disassembled okay but the data references were mostly off. So I looked at the strings to see if there may be some candidates for a string table. This bunch looked promising:
ROM:0030D8F3 aLanguageZoneAl:.ascii "LANGUAGE ZONE : ALL"
ROM:0030D8F3 .byte 0
ROM:0030D907 aLanguageZoneJp:.ascii "LANGUAGE ZONE : JP"
ROM:0030D907 .byte 0
ROM:0030D91A aLanguageZoneUs:.ascii "LANGUAGE ZONE : US"
ROM:0030D91A .byte 0
ROM:0030D92D aLanguageZoneEu:.ascii "LANGUAGE ZONE : EU"
ROM:0030D92D .byte 0
Now, the proper way would be to write a script that would scan the database for a dword array with values having the same difference as string addresses. However, I was lazy so I though to myself, "The image base/shift value is likely a multiple of 0x100, so the last byte of the address will be the same, i.e. I need to look for a table like":
.. .. .. F3
.. .. .. 07
.. .. .. 1A
This can be easily done via Binary search with the pattern "F3 ? ? ? 07 ? ? ? 1A" (without quotes; '?' denotes a wildcard byte). There was only one hit:
ROM:001A8DC4 .long unk_35A0F3
ROM:001A8DC8 .long unk_35A107
ROM:001A8DCC .long unk_35A11A
So if the first string should be at 35A0F3, we need to rebase the binary by 0x35A0F3-0x30D8F3 = 0x4C800 bytes. This can be done via Edit-Segment-Rebase program..., Shift delta. Once done, our table is nicely displayed, and additional strings line up too:
ROM:001F55C4 .long aLanguageZoneAl # "LANGUAGE ZONE : ALL"
ROM:001F55C8 .long aLanguageZoneJp # "LANGUAGE ZONE : JP"
ROM:001F55CC .long aLanguageZoneUs # "LANGUAGE ZONE : US"
ROM:001F55D0 .long aLanguageZoneEu # "LANGUAGE ZONE : EU"
ROM:001F55D4 .long aLanguageZoneEg # "LANGUAGE ZONE : EG"
ROM:001F55D8 .long aLanguageZoneEe # "LANGUAGE ZONE : EE"
ROM:001F55DC .long aLanguageZoneE1 # "LANGUAGE ZONE : E1"
ROM:001F55E0 .long aLanguageZoneCh # "LANGUAGE ZONE : CH"
ROM:001F55E4 .long aLanguageZoneUn # "LANGUAGE ZONE : Unknown"
However, this makes the code start at 4C800 and there is no such value in the image header which made me suspicious it's not the full picture. So I decided to look for code which parses the headers. A good start for this is looking for magic values (like header signatures).
Looking for 52 48 49 52 ("RHIR") did not give any hits (neither did the byte-swapped version). However, I remembered that ARCompact uses a peculiar way of encoding 32-bit immediates (aka "long immediates"): they are stored in mixed endian form (high word followed by low word). Searching for 49 52 52 48, I got one hit and after backing up to find the function start I got the following:
ROM:0018538E sub_18538E:
ROM:0018538E
ROM:0018538E var_4= -4
ROM:0018538E
ROM:0018538E st.a fp, [sp,var_4]
ROM:00185392 mov fp, sp
ROM:00185396 sub sp, sp, 4
ROM:00185398 mov r18, r0
ROM:0018539A mov r16, r1
ROM:0018539C sub r1, fp, 4
ROM:001853A0 mov r0, 6
ROM:001853A2 bl sub_6CC44
ROM:001853A6 ld r0, [fp,var_4]
ROM:001853AA cmp r0, r16
ROM:001853AC bhs loc_1853B2
ROM:001853AE ld r0, =0x1C00114
ROM:001853B0 b loc_18541E
ROM:001853B2 # ---------------------------
ROM:001853B2
ROM:001853B2 loc_1853B2:
ROM:001853B2 ld r0, [r18]
ROM:001853B6 mov r13, r18
ROM:001853B8 mov r17, r18
ROM:001853BC cmp r0, 0x52494852
ROM:001853C2 bne loc_1853D8
ROM:001853C4 ld r0, [r17,8]
ROM:001853C8 and r20, r0, 0xFFFF00
ROM:001853D0 bl sub_50814
ROM:001853D4 cmp r0, r20
ROM:001853D6 beq loc_1853DC
ROM:001853D8
ROM:001853D8 loc_1853D8:
ROM:001853D8 ld r0, =0x1C00111
ROM:001853DA b loc_18541E
ROM:001853DC # ---------------------------
ROM:001853DC
ROM:001853DC loc_1853DC:
ROM:001853DC add r13, r13, 0x10
ROM:001853DE mov r15, 0
ROM:001853E0
ROM:001853E0 loc_1853E0:
ROM:001853E0 ldb r0, [r17,6]
ROM:001853E4 cmp r15, r0
ROM:001853E6 bge loc_18541C
ROM:001853EA ldw r0, [r13]
ROM:001853EC mov r14, r13
ROM:001853EE cmp r0, 1
ROM:001853F0 bne loc_185416
ROM:001853F2 ldw r1, [r14,4]
ROM:001853F4 ldw r2, [r14,6]
ROM:001853F6 asl r1, r1, 9
ROM:001853F8 asl r2, r2, 9
ROM:001853FA mov r19, r1
ROM:001853FC add r0, r1, r18
ROM:00185400 st r2, [fp,var_4]
ROM:00185404 mov r1, r2
ROM:00185406 bl sub_24C9A4
ROM:0018540A ld r1, [r14,0xC]
ROM:0018540C cmp r0, r1
ROM:0018540E beq loc_185414
ROM:00185410 ld r0, =0x1C00111
ROM:00185412 b loc_18541E
ROM:00185414 # ---------------------------
ROM:00185414
ROM:00185414 loc_185414:
ROM:00185414 add r13, r13, 0x10
ROM:00185416
ROM:00185416 loc_185416:
ROM:00185416 add r15, r15, 1
ROM:00185418 sexw r15, r15
ROM:0018541A b loc_1853E0
ROM:0018541C # ---------------------------
ROM:0018541C
ROM:0018541C loc_18541C:
ROM:0018541C mov r0, 0
ROM:0018541E
ROM:0018541E loc_18541E:
ROM:0018541E
ROM:0018541E mov sp, fp
ROM:00185422 ld.ab fp, [sp,8+var_4]
ROM:00185426 b __ac_pop_13_to_20
ROM:00185426 # End of function sub_18538E
We can clearly see the check against 0x52494852
(or "RHIR"), so it seems r18
points to the start of the header. After some reversing the header structure seems to be like this:
struct ImageHeader
{
/* 00 */ uint32_t Signature;
/* 04 */ uint8_t unk4[2];
/* 06 */ uint8_t nblocks;
/* 07 */ uint8_t unk7[1];
/* 08 */ uint32_t platform;
/* 0C */ uint32_t unkC;
};
followed by an array of blocks:
struct ImageBlock
{
/* 00 */ uint16_t type;
/* 02 */ uint8_t pad2[2];
/* 04 */ uint16_t start;
/* 06 */ uint16_t size;
/* 08 */ uint32_t unk8;
/* 0C */ uint32_t checksum;
};
the code is checking for blocks of type==1, for which it uses start<<9
as an offset from the header start and size<<9
as the size of the block to calculate the checksum and compare against checksum
field.
I made a quick script to parse and print the header and here's the output:
unk4: 00001001
blocks: 7
platform: 00050600
unkC: 01A40001
block 0 type: 1
offset: 1 (200)
size: 14dc (29b800)
unk8: 00048000
checksum: A1AA045A
block 1 type: 1
offset: 14dd (29ba00)
size: 731 (e6200)
unk8: 002E8000
checksum: 917C608A
block 2 type: 0
offset: 1c0e (381c00)
size: 54c (a9800)
unk8: 00430000
checksum: EA1137CD
block 3 type: 1
offset: 215a (42b400)
size: 21 (4200)
unk8: 004DA000
checksum: 8AE264F9
block 4 type: 0
offset: 217b (42f600)
size: 2a6 (54c00)
unk8: 004DA000
checksum: 8606D33F
block 5 type: 0
offset: 2422 (484400)
size: 34 (6800)
unk8: 004DA000
checksum: 802A3C3B
block 6 type: 2
offset: b420 (1684000)
size: 4 (800)
unk8: 00000000
checksum: 00000000
So it seems we have these chunks :
# type offset size unk8
------------------------------
0 1 200 29b800 00048000
1 1 29ba00 e6200 002E8000
2 0 381c00 a9800 00430000
3 1 42b400 4200 004DA000
4 0 42f600 54c00 004DA000
5 0 484400 6800 004DA000
6 2 1684000 800 00000000
unk8 looks very much like a memory address, and indeed if we check the string "LANGUAGE ZONE : ALL" which is at 30DAF3 in the file, this corresponds to chunk #1 and is at the same offset from its start (720F3) as our string in database(0035A0F3) from the hypothetical memory address 002E8000. After splitting the loaded data according to the table and moving chunks of type 1 to the correct addresses, I've got this layout:
CHUNK0 00048000 002E3800
CHUNK1 002E8000 003CE200
CHUNK3 004DA000 004DE200