2018-07-22 20:22:10 +00:00
|
|
|
/*
|
2018-08-05 11:40:32 +00:00
|
|
|
* Copyright (c) 2018 rajkosto
|
|
|
|
* Copyright (c) 2018 SciresM
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
|
|
* under the terms and conditions of the GNU General Public License,
|
|
|
|
* version 2, as published by the Free Software Foundation.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
|
|
* more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
2018-07-22 20:22:10 +00:00
|
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
2018-08-05 11:40:32 +00:00
|
|
|
|
2018-07-22 20:22:10 +00:00
|
|
|
#include "blz.h"
|
|
|
|
|
2024-06-02 03:51:47 +00:00
|
|
|
const blz_footer *blz_get_footer(const u8 *comp_data, u32 comp_data_size, blz_footer *out_footer)
|
2018-07-22 20:22:10 +00:00
|
|
|
{
|
2024-06-02 03:51:47 +00:00
|
|
|
if (comp_data_size < sizeof(blz_footer))
|
2018-07-22 20:22:10 +00:00
|
|
|
return NULL;
|
|
|
|
|
2024-06-02 03:51:47 +00:00
|
|
|
const blz_footer *src_footer = (const blz_footer *)&comp_data[comp_data_size - sizeof(blz_footer)];
|
|
|
|
if (out_footer)
|
|
|
|
memcpy(out_footer, src_footer, sizeof(blz_footer)); // Must be a memcpy because no unaligned accesses on ARMv4.
|
2018-07-22 20:22:10 +00:00
|
|
|
|
2024-06-02 03:51:47 +00:00
|
|
|
return src_footer;
|
2018-07-22 20:22:10 +00:00
|
|
|
}
|
|
|
|
|
2018-08-05 11:40:32 +00:00
|
|
|
// From https://github.com/SciresM/hactool/blob/master/kip.c which is exactly how kernel does it, thanks SciresM!
|
2024-06-02 03:51:47 +00:00
|
|
|
int blz_uncompress_inplace(u8 *data, u32 comp_size, const blz_footer *footer)
|
2018-07-22 20:22:10 +00:00
|
|
|
{
|
2018-08-05 11:40:32 +00:00
|
|
|
u32 addl_size = footer->addl_size;
|
|
|
|
u32 header_size = footer->header_size;
|
|
|
|
u32 cmp_and_hdr_size = footer->cmp_and_hdr_size;
|
2019-10-18 15:02:06 +00:00
|
|
|
|
2024-06-02 03:51:47 +00:00
|
|
|
u8 *cmp_start = &data[comp_size] - cmp_and_hdr_size;
|
2018-08-05 11:40:32 +00:00
|
|
|
u32 cmp_ofs = cmp_and_hdr_size - header_size;
|
|
|
|
u32 out_ofs = cmp_and_hdr_size + addl_size;
|
2019-10-18 15:02:06 +00:00
|
|
|
|
|
|
|
while (out_ofs)
|
2018-07-22 20:22:10 +00:00
|
|
|
{
|
2024-06-02 03:51:47 +00:00
|
|
|
u8 control = cmp_start[--cmp_ofs];
|
|
|
|
for (u32 i = 0; i < 8; i++)
|
2018-07-22 20:22:10 +00:00
|
|
|
{
|
2019-10-18 15:02:06 +00:00
|
|
|
if (control & 0x80)
|
2018-07-22 20:22:10 +00:00
|
|
|
{
|
2019-10-18 15:02:06 +00:00
|
|
|
if (cmp_ofs < 2)
|
2018-08-05 11:40:32 +00:00
|
|
|
return 0; // Out of bounds.
|
|
|
|
|
|
|
|
cmp_ofs -= 2;
|
2024-06-02 03:51:47 +00:00
|
|
|
u16 seg_val = ((u32)(cmp_start[cmp_ofs + 1]) << 8) | cmp_start[cmp_ofs];
|
2018-08-05 11:40:32 +00:00
|
|
|
u32 seg_size = ((seg_val >> 12) & 0xF) + 3;
|
|
|
|
u32 seg_ofs = (seg_val & 0x0FFF) + 3;
|
2024-06-02 03:51:47 +00:00
|
|
|
|
|
|
|
// Kernel restricts segment copy to stay in bounds.
|
|
|
|
if (out_ofs < seg_size)
|
2018-08-05 11:40:32 +00:00
|
|
|
seg_size = out_ofs;
|
2018-07-22 20:22:10 +00:00
|
|
|
|
2018-08-05 11:40:32 +00:00
|
|
|
out_ofs -= seg_size;
|
2018-07-22 20:22:10 +00:00
|
|
|
|
2024-06-02 03:51:47 +00:00
|
|
|
for (u32 j = 0; j < seg_size; j++)
|
2018-08-05 11:40:32 +00:00
|
|
|
cmp_start[out_ofs + j] = cmp_start[out_ofs + j + seg_ofs];
|
|
|
|
}
|
2024-06-02 03:51:47 +00:00
|
|
|
else // Copy directly.
|
2018-07-22 20:22:10 +00:00
|
|
|
{
|
2019-10-18 15:02:06 +00:00
|
|
|
if (cmp_ofs < 1)
|
2024-06-02 03:51:47 +00:00
|
|
|
return 0; // Out of bounds.
|
2018-07-22 20:22:10 +00:00
|
|
|
|
2018-08-05 11:40:32 +00:00
|
|
|
cmp_start[--out_ofs] = cmp_start[--cmp_ofs];
|
|
|
|
}
|
2024-06-02 03:51:47 +00:00
|
|
|
|
2018-08-05 11:40:32 +00:00
|
|
|
control <<= 1;
|
2024-06-02 03:51:47 +00:00
|
|
|
|
|
|
|
if (!out_ofs) // Blz works backwards, so if it reaches byte 0, it's done.
|
2019-10-18 15:02:06 +00:00
|
|
|
return 1;
|
2018-08-05 11:40:32 +00:00
|
|
|
}
|
2024-06-02 03:51:47 +00:00
|
|
|
}
|
2018-07-22 20:22:10 +00:00
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2024-06-02 03:51:47 +00:00
|
|
|
int blz_uncompress_srcdest(const u8 *comp_data, u32 comp_data_size, u8 *dst_data, u32 dst_size)
|
2018-07-22 20:22:10 +00:00
|
|
|
{
|
2018-08-05 11:40:32 +00:00
|
|
|
blz_footer footer;
|
2024-06-02 03:51:47 +00:00
|
|
|
const blz_footer *comp_footer = blz_get_footer(comp_data, comp_data_size, &footer);
|
|
|
|
if (!comp_footer)
|
2018-08-05 11:40:32 +00:00
|
|
|
return 0;
|
2018-07-22 20:22:10 +00:00
|
|
|
|
2024-06-02 03:51:47 +00:00
|
|
|
// Decompression happens in-place, so need to copy the relevant compressed data first.
|
|
|
|
u32 comp_bytes = (const u8 *)comp_footer - comp_data;
|
|
|
|
memcpy(dst_data, comp_data, comp_bytes);
|
|
|
|
memset(&dst_data[comp_bytes], 0, dst_size - comp_bytes);
|
2018-07-22 20:22:10 +00:00
|
|
|
|
2024-06-02 03:51:47 +00:00
|
|
|
return blz_uncompress_inplace(dst_data, comp_data_size, &footer);
|
2018-08-05 11:40:32 +00:00
|
|
|
}
|