Version 2.4 (2021-12-08)

- Updated source code from Eric Rechlin's Emu48 version 1.63+ that was merged from Christoph Gießelink's Emu48 version 1.64.
This commit is contained in:
dgis 2021-12-12 09:48:47 +01:00
parent 1b26b74f48
commit 8a54fb0b33
14 changed files with 365 additions and 187 deletions

View file

@ -58,6 +58,11 @@ LINKS
CHANGES CHANGES
Version 2.4 (2021-12-08)
- Updated source code from Eric Rechlin's Emu48 version 1.63+ that was merged from Christoph Gießelink's Emu48 version 1.64.
Version 2.3 (2021-10-19) Version 2.3 (2021-10-19)
- Add an experimental serial port support (via USB OTG). - Add an experimental serial port support (via USB OTG).
@ -251,7 +256,7 @@ TODO
BUILD BUILD
Emu48 for Android is built with Android Studio 4.1 (2020). Emu48 for Android is built with Android Studio 2020.3.1 (2021).
And to generate an installable APK file with a real Android device, it MUST be signed. And to generate an installable APK file with a real Android device, it MUST be signed.
Either use Android Studio: Either use Android Studio:

View file

@ -33,8 +33,8 @@ android {
applicationId "org.emulator.forty.eight" applicationId "org.emulator.forty.eight"
minSdkVersion 19 minSdkVersion 19
targetSdkVersion 30 targetSdkVersion 30
versionCode 22 versionCode 23
versionName "2.3" versionName "2.4"
setProperty("archivesBaseName", "Emu48-v$versionName") setProperty("archivesBaseName", "Emu48-v$versionName")
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
externalNativeBuild { externalNativeBuild {

View file

@ -114,6 +114,21 @@ h3 { color:red; font-size:1.1em; }
into the native Emu48 ROM format is not necessary any more. You can into the native Emu48 ROM format is not necessary any more. You can
still use the classic way extracting them from your own calculator. still use the classic way extracting them from your own calculator.
</p> </p>
<p>The command line <i>Convert</i> utility delivered with the Emu48
program package was originally designed to convert HP48 ROM images
files from one of the various used ROM image formats of the 90s to
the unpacked format originally used by Emu48. The 2nd important
task of this tool is updating the I/O register area with zeros.
Most images had been created with a ROM upload program where the
I/O register area was mapped over the ROM content and so the ROM
image file contain a wrong content at this position. Executing the
selftest on a HP38 or a HP48 will report an IROM fail then. To fix
this the <i>Convert</i> utility overwrites the I/O register area
in the destination file with zeros. Final notice, the convert
utility shows the CRC result after the file convert and a passed
ROM CRC test does <u><b>not</b></u> imply, that the source file
is in an Emu48 suitable format!
</p>
<ul> <ul>
<li>HP38 <li>HP38
<p>To upload the ROM of your HP38G, you will need a special aplet <p>To upload the ROM of your HP38G, you will need a special aplet
@ -636,10 +651,7 @@ h3 { color:red; font-size:1.1em; }
Cyrille de Brebisson of Hewlett-Packard.</p> Cyrille de Brebisson of Hewlett-Packard.</p>
<h1><a name=s15>15. License</a></h1> <h1><a name=s15>15. License</a></h1>
<p>Emu48 - A HP38G/39G/40G/48SX/48GX/49G Emulator<br> <p>Emu48 - A HP38G/39G/40G/48SX/48GX/49G Emulator<br>
Copyright (C) 2020 Christoph Gie&szlig;elink</p> Copyright (C) 2021 Christoph Gie&szlig;elink</p>
<p>Emu48+ - A 39g+/39gs/40gs/48gII/49g+/50g Emulator<br>
Copyright (C) 2020 Cyrille de Brebisson<br>
Additional changes by Bill Graves and Eric Rechlin</p>
<p>This program is free software; you can redistribute it and/or modify it <p>This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free under the terms of the GNU General Public License as published by the Free
Software Foundation; either version 2 of the License, or (at your option) Software Foundation; either version 2 of the License, or (at your option)

View file

@ -58,6 +58,11 @@ LINKS
CHANGES CHANGES
Version 2.4 (2021-12-08)
- Updated source code from Eric Rechlin's Emu48 version 1.63+ that was merged from Christoph Gießelink's Emu48 version 1.64.
Version 2.3 (2021-10-19) Version 2.3 (2021-10-19)
- Add an experimental serial port support (via USB OTG). - Add an experimental serial port support (via USB OTG).

View file

@ -13,7 +13,7 @@
#include "kml.h" #include "kml.h"
#include "debugger.h" #include "debugger.h"
#define VERSION "1.62+" #define VERSION "1.63+"
#ifdef _DEBUG #ifdef _DEBUG
LPCTSTR szNoTitle = _T("Emu48 ")_T(VERSION)_T(" Debug"); LPCTSTR szNoTitle = _T("Emu48 ")_T(VERSION)_T(" Debug");
@ -1753,26 +1753,40 @@ static LRESULT OnAbout(VOID)
static VOID OnContextMenu(LPARAM lParam) static VOID OnContextMenu(LPARAM lParam)
{ {
HMENU hMenu;
POINT pt,ptc;
if (GetMenu(hWnd) == NULL) // no main window menu if (GetMenu(hWnd) == NULL) // no main window menu
{ {
BOOL bContextMenu = TRUE; // call context menu
POINT pt;
POINTSTOPOINT(pt,MAKEPOINTS(lParam)); // mouse position POINTSTOPOINT(pt,MAKEPOINTS(lParam)); // mouse position
if (pt.x == -1 && pt.y == -1) // VK_APPS if (pt.x == -1 && pt.y == -1) // VK_APPS
{
RECT rc;
GetCursorPos(&pt); // get current mouse position
GetWindowRect(hWnd,&rc); // get position of active window
if (PtInRect(&rc,pt)==FALSE) // mouse position outside active window
{ {
pt.x = 15; // open context help at client position 15,15 pt.x = 15; // open context help at client position 15,15
pt.y = 15; pt.y = 15;
VERIFY(ClientToScreen(hWnd,&pt)); VERIFY(ClientToScreen(hWnd,&pt));
} }
}
ptc = pt; else // got a mouse position
VERIFY(ScreenToClient(hWnd,&ptc)); // convert mouse into client position {
POINT ptc = pt;
// convert mouse into client position
VERIFY(ScreenToClient(hWnd,&ptc));
// in client area not over a button // in client area not over a button
if (ptc.y >= 0 && !MouseIsButton(ptc.x,ptc.y)) bContextMenu = (ptc.y >= 0 && !MouseIsButton(ptc.x,ptc.y));
}
if (bContextMenu) // call the context menu
{ {
HMENU hMenu;
// load the popup menu resource // load the popup menu resource
if ((hMenu = LoadMenu(hApp,MAKEINTRESOURCE(IDM_MENU))) != NULL) if ((hMenu = LoadMenu(hApp,MAKEINTRESOURCE(IDM_MENU))) != NULL)
{ {

View file

@ -2901,8 +2901,6 @@ static INT_PTR CALLBACK EnterBreakpoint(HWND hDlg, UINT message, WPARAM wParam,
{ {
static BP_T *sBp; static BP_T *sBp;
DWORD dwAddr;
switch (message) switch (message)
{ {
case WM_INITDIALOG: case WM_INITDIALOG:
@ -2921,9 +2919,8 @@ static INT_PTR CALLBACK EnterBreakpoint(HWND hDlg, UINT message, WPARAM wParam,
case IDC_BPREAD: sBp->nType = BP_READ; return TRUE; case IDC_BPREAD: sBp->nType = BP_READ; return TRUE;
case IDC_BPWRITE: sBp->nType = BP_WRITE; return TRUE; case IDC_BPWRITE: sBp->nType = BP_WRITE; return TRUE;
case IDOK: case IDOK:
if (!GetAddr(hDlg,IDC_ENTERADR,&dwAddr,0xFFFFF,disassembler_symb)) if (!GetAddr(hDlg,IDC_ENTERADR,&sBp->dwAddr,0xFFFFF,disassembler_symb))
return FALSE; return FALSE;
sBp->dwAddr = dwAddr;
// no break // no break
case IDCANCEL: case IDCANCEL:
EndDialog(hDlg,wParam); EndDialog(hDlg,wParam);
@ -2955,42 +2952,35 @@ static __inline BOOL OnDrawBreakWnd(LPDRAWITEMSTRUCT lpdis)
COLORREF crBkColor,crTextColor; COLORREF crBkColor,crTextColor;
HDC hdcMem; HDC hdcMem;
HBITMAP hBmpOld; HBITMAP hBmpOld;
INT i;
if (lpdis->itemID == -1) // no item in list box if (lpdis->itemID == -1) // no item in list box
return TRUE; return TRUE;
crBkColor = GetBkColor(lpdis->hDC); // save actual color settings
crTextColor = GetTextColor(lpdis->hDC);
if (lpdis->itemState & ODS_SELECTED) // cursor line if (lpdis->itemState & ODS_SELECTED) // cursor line
{ {
crBkColor = COLOR_NAVY; SetBkColor(lpdis->hDC,GetSysColor(COLOR_HIGHLIGHT));
crTextColor = COLOR_WHITE; SetTextColor(lpdis->hDC,GetSysColor(COLOR_HIGHLIGHTTEXT));
}
else
{
crBkColor = COLOR_WHITE;
crTextColor = COLOR_BLACK;
} }
// write Text // write Text
crBkColor = SetBkColor(lpdis->hDC,crBkColor);
crTextColor = SetTextColor(lpdis->hDC,crTextColor);
SendMessage(lpdis->hwndItem,LB_GETTEXT,lpdis->itemID,(LPARAM) szBuf); SendMessage(lpdis->hwndItem,LB_GETTEXT,lpdis->itemID,(LPARAM) szBuf);
ExtTextOut(lpdis->hDC,(int)(lpdis->rcItem.left)+17,(int)(lpdis->rcItem.top), ExtTextOut(lpdis->hDC,(int)(lpdis->rcItem.left)+17,(int)(lpdis->rcItem.top),
ETO_OPAQUE,(LPRECT)&lpdis->rcItem,szBuf,lstrlen(szBuf),NULL); ETO_OPAQUE,(LPRECT)&lpdis->rcItem,szBuf,lstrlen(szBuf),NULL);
SetBkColor(lpdis->hDC,crBkColor); SetBkColor(lpdis->hDC,crBkColor); // restore color settings
SetTextColor(lpdis->hDC,crTextColor); SetTextColor(lpdis->hDC,crTextColor);
// draw checkbox // draw checkbox
i = (INT) SendMessage(lpdis->hwndItem,LB_GETITEMDATA,lpdis->itemID,0);
hdcMem = CreateCompatibleDC(lpdis->hDC); hdcMem = CreateCompatibleDC(lpdis->hDC);
_ASSERT(hBmpCheckBox); _ASSERT(hBmpCheckBox);
hBmpOld = (HBITMAP) SelectObject(hdcMem,hBmpCheckBox); hBmpOld = (HBITMAP) SelectObject(hdcMem,hBmpCheckBox);
BitBlt(lpdis->hDC,lpdis->rcItem.left+2,lpdis->rcItem.top+2, BitBlt(lpdis->hDC,lpdis->rcItem.left+2,lpdis->rcItem.top+2,
11,lpdis->rcItem.bottom - lpdis->rcItem.top, 11,lpdis->rcItem.bottom - lpdis->rcItem.top,
hdcMem,sBreakpoint[i].bEnable ? 0 : 10,0,SRCCOPY); hdcMem,sBreakpoint[lpdis->itemData].bEnable ? 0 : 10,0,SRCCOPY);
SelectObject(hdcMem,hBmpOld); SelectObject(hdcMem,hBmpOld);
DeleteDC(hdcMem); DeleteDC(hdcMem);

View file

@ -425,8 +425,8 @@ static LPTSTR disasm_1 (DWORD *addr, LPTSTR out)
fn = read_nibble (addr); fn = read_nibble (addr);
c = (fn < 8); // flag for operand register c = (fn < 8); // flag for operand register
fn = (fn & 7); // get register number fn = (fn & 7); // get register number
if (fn > 4) // illegal opcode if (fn > 4) // unsupported opcode
break; // no output fn -= 4; // map to valid scratch register
switch (disassembler_mode) switch (disassembler_mode)
{ {
case HP_MNEMONICS: case HP_MNEMONICS:
@ -457,8 +457,8 @@ static LPTSTR disasm_1 (DWORD *addr, LPTSTR out)
fn = read_nibble (addr); fn = read_nibble (addr);
c = (fn < 8); // flag for operand register c = (fn < 8); // flag for operand register
fn = (fn & 7); // get register number fn = (fn & 7); // get register number
if (fn > 4) // illegal opcode if (fn > 4) // unsupported opcode
break; // no output fn -= 4; // map to valid scratch register
switch (disassembler_mode) switch (disassembler_mode)
{ {
case HP_MNEMONICS: case HP_MNEMONICS:
@ -680,6 +680,7 @@ static LPTSTR disasm_8 (DWORD *addr, LPTSTR out)
{ {
BYTE n; BYTE n;
BYTE fn; BYTE fn;
BYTE rn;
LPTSTR p = out; LPTSTR p = out;
TCHAR c; TCHAR c;
TCHAR buf[20]; TCHAR buf[20];
@ -975,48 +976,49 @@ static LPTSTR disasm_8 (DWORD *addr, LPTSTR out)
if (n > 2) // illegal opcode if (n > 2) // illegal opcode
break; // no output break; // no output
c = (TCHAR) read_nibble (addr); c = (TCHAR) read_nibble (addr);
if (((int) c & 7) > 4) // illegal opcode rn = (c & 7); // get register number
break; // no output c = (c < 8); // flag for operand register
if (rn > 4) // unsupported opcode
rn -= 4; // map to valid scratch register
switch (disassembler_mode) switch (disassembler_mode)
{ {
case HP_MNEMONICS: case HP_MNEMONICS:
c = (TCHAR) (c ? _T('A') : _T('C'));
if (n == 2) if (n == 2)
{ {
wsprintf (buf, _T("%cR%dEX.F"), ((int) c < 8) ? _T('A') : _T('C'), wsprintf (buf, _T("%cR%dEX.F"), c, rn);
(int) c & 7);
} }
else else
if (n == 1) if (n == 1)
{ {
wsprintf (buf, _T("%c=R%d.F"), ((int) c < 8) ? _T('A') : _T('C'), wsprintf (buf, _T("%c=R%d.F"), c, rn);
(int) c & 7);
} }
else else
{ {
wsprintf (buf, _T("R%d=%c.F"), (int) c & 7, wsprintf (buf, _T("R%d=%c.F"), rn, c);
((int) c < 8) ? _T('A') : _T('C'));
} }
p = append_str (out, buf); p = append_str (out, buf);
p = append_tab (out); p = append_tab (out);
p = append_field (p, fn); p = append_field (p, fn);
break; break;
case CLASS_MNEMONICS: case CLASS_MNEMONICS:
c = (TCHAR) (c ? _T('a') : _T('c'));
p = append_str (out, (n == 2) ? _T("exg") : _T("move")); p = append_str (out, (n == 2) ? _T("exg") : _T("move"));
p = append_field (p, fn); p = append_field (p, fn);
p = append_tab (out); p = append_tab (out);
if (n == 1) if (n == 1)
{ {
wsprintf (buf, _T("r%d"), (int) c & 7); wsprintf (buf, _T("r%d"), rn);
p = append_str (p, buf); p = append_str (p, buf);
} }
else else
p = append_str (p, ((int) c < 8) ? _T("a") : _T("c")); *p++ = c;
p = append_str (p, _T(", ")); p = append_str (p, _T(", "));
if (n == 1) if (n == 1)
p = append_str (p, ((int) c < 8) ? _T("a") : _T("c")); *p++ = c;
else else
{ {
wsprintf (buf, _T("r%d"), (int) c & 7); wsprintf (buf, _T("r%d"), rn);
p = append_str (p, buf); p = append_str (p, buf);
} }
break; break;

View file

@ -373,11 +373,16 @@ VOID InitAdjustSpeed(VOID)
if (!bEnableSlow || (!bCpuSlow && !bKeySlow && !bSoundSlow && nOpcSlow == 0)) if (!bEnableSlow || (!bCpuSlow && !bKeySlow && !bSoundSlow && nOpcSlow == 0))
{ {
LARGE_INTEGER lTime; // sample timer ticks LARGE_INTEGER lTime; // sample timer ticks
EnterCriticalSection(&csSlowLock);
{
// save reference cycles // save reference cycles
dwOldCyc = (DWORD) (Chipset.cycles & 0xFFFFFFFF); dwOldCyc = (DWORD) (Chipset.cycles & 0xFFFFFFFF);
QueryPerformanceCounter(&lTime); // get timer ticks QueryPerformanceCounter(&lTime);// get timer ticks
dwSpeedRef = lTime.LowPart; // save reference time dwSpeedRef = lTime.LowPart; // save reference time
} }
LeaveCriticalSection(&csSlowLock);
}
return; return;
} }
@ -390,29 +395,21 @@ VOID AdjKeySpeed(VOID) // slow down key repeat
for (i = 0;i < ARRAYSIZEOF(Chipset.Keyboard_Row) && !bKey;++i) for (i = 0;i < ARRAYSIZEOF(Chipset.Keyboard_Row) && !bKey;++i)
bKey = (Chipset.Keyboard_Row[i] != 0); bKey = (Chipset.Keyboard_Row[i] != 0);
EnterCriticalSection(&csSlowLock);
{
if (bKey) // key pressed if (bKey) // key pressed
{ {
InitAdjustSpeed(); // init variables if necessary InitAdjustSpeed(); // init variables if necessary
} }
bKeySlow = bKey; // save new state bKeySlow = bKey; // save new state
}
LeaveCriticalSection(&csSlowLock);
return; return;
} }
VOID SetSpeed(BOOL bAdjust) // set emulation speed VOID SetSpeed(BOOL bAdjust) // set emulation speed
{ {
EnterCriticalSection(&csSlowLock);
{
if (bAdjust) // switch to real speed if (bAdjust) // switch to real speed
{ {
InitAdjustSpeed(); // init variables if necessary InitAdjustSpeed(); // init variables if necessary
} }
bCpuSlow = bAdjust; // save emulation speed bCpuSlow = bAdjust; // save emulation speed
}
LeaveCriticalSection(&csSlowLock);
return; return;
} }

View file

@ -345,17 +345,17 @@ static JMPTAB o81Af2_[] =
(LPCVOID) o81Af22, F, (LPCVOID) o81Af22, F,
(LPCVOID) o81Af23, F, (LPCVOID) o81Af23, F,
(LPCVOID) o81Af24, F, (LPCVOID) o81Af24, F,
(LPCVOID) o_invalid6, F, (LPCVOID) o81Af21, F,
(LPCVOID) o_invalid6, F, (LPCVOID) o81Af22, F,
(LPCVOID) o_invalid6, F, (LPCVOID) o81Af23, F,
(LPCVOID) o81Af28, F, (LPCVOID) o81Af28, F,
(LPCVOID) o81Af29, F, (LPCVOID) o81Af29, F,
(LPCVOID) o81Af2A, F, (LPCVOID) o81Af2A, F,
(LPCVOID) o81Af2B, F, (LPCVOID) o81Af2B, F,
(LPCVOID) o81Af2C, F, (LPCVOID) o81Af2C, F,
(LPCVOID) o_invalid6, F, (LPCVOID) o81Af29, F,
(LPCVOID) o_invalid6, F, (LPCVOID) o81Af2A, F,
(LPCVOID) o_invalid6, F (LPCVOID) o81Af2B, F
}; };
static JMPTAB o81Af1_[] = static JMPTAB o81Af1_[] =
@ -365,17 +365,17 @@ static JMPTAB o81Af1_[] =
(LPCVOID) o81Af12, F, (LPCVOID) o81Af12, F,
(LPCVOID) o81Af13, F, (LPCVOID) o81Af13, F,
(LPCVOID) o81Af14, F, (LPCVOID) o81Af14, F,
(LPCVOID) o_invalid6, F, (LPCVOID) o81Af11, F,
(LPCVOID) o_invalid6, F, (LPCVOID) o81Af12, F,
(LPCVOID) o_invalid6, F, (LPCVOID) o81Af13, F,
(LPCVOID) o81Af18, F, (LPCVOID) o81Af18, F,
(LPCVOID) o81Af19, F, (LPCVOID) o81Af19, F,
(LPCVOID) o81Af1A, F, (LPCVOID) o81Af1A, F,
(LPCVOID) o81Af1B, F, (LPCVOID) o81Af1B, F,
(LPCVOID) o81Af1C, F, (LPCVOID) o81Af1C, F,
(LPCVOID) o_invalid6, F, (LPCVOID) o81Af19, F,
(LPCVOID) o_invalid6, F, (LPCVOID) o81Af1A, F,
(LPCVOID) o_invalid6, F (LPCVOID) o81Af1B, F
}; };
static JMPTAB o81Af0_[] = static JMPTAB o81Af0_[] =
@ -385,17 +385,17 @@ static JMPTAB o81Af0_[] =
(LPCVOID) o81Af02, F, (LPCVOID) o81Af02, F,
(LPCVOID) o81Af03, F, (LPCVOID) o81Af03, F,
(LPCVOID) o81Af04, F, (LPCVOID) o81Af04, F,
(LPCVOID) o_invalid6, F, (LPCVOID) o81Af01, F,
(LPCVOID) o_invalid6, F, (LPCVOID) o81Af02, F,
(LPCVOID) o_invalid6, F, (LPCVOID) o81Af03, F,
(LPCVOID) o81Af08, F, (LPCVOID) o81Af08, F,
(LPCVOID) o81Af09, F, (LPCVOID) o81Af09, F,
(LPCVOID) o81Af0A, F, (LPCVOID) o81Af0A, F,
(LPCVOID) o81Af0B, F, (LPCVOID) o81Af0B, F,
(LPCVOID) o81Af0C, F, (LPCVOID) o81Af0C, F,
(LPCVOID) o_invalid6, F, (LPCVOID) o81Af09, F,
(LPCVOID) o_invalid6, F, (LPCVOID) o81Af0A, F,
(LPCVOID) o_invalid6, F (LPCVOID) o81Af0B, F
}; };
static JMPTAB o81A_[] = static JMPTAB o81A_[] =
@ -625,17 +625,17 @@ static JMPTAB o12_[] =
(LPCVOID) o122, F, (LPCVOID) o122, F,
(LPCVOID) o123, F, (LPCVOID) o123, F,
(LPCVOID) o124, F, (LPCVOID) o124, F,
(LPCVOID) o_invalid3, F, (LPCVOID) o121, F,
(LPCVOID) o_invalid3, F, (LPCVOID) o122, F,
(LPCVOID) o_invalid3, F, (LPCVOID) o123, F,
(LPCVOID) o128, F, (LPCVOID) o128, F,
(LPCVOID) o129, F, (LPCVOID) o129, F,
(LPCVOID) o12A, F, (LPCVOID) o12A, F,
(LPCVOID) o12B, F, (LPCVOID) o12B, F,
(LPCVOID) o12C, F, (LPCVOID) o12C, F,
(LPCVOID) o_invalid3, F, (LPCVOID) o129, F,
(LPCVOID) o_invalid3, F, (LPCVOID) o12A, F,
(LPCVOID) o_invalid3, F (LPCVOID) o12B, F
}; };
static JMPTAB o11_[] = static JMPTAB o11_[] =
@ -645,17 +645,17 @@ static JMPTAB o11_[] =
(LPCVOID) o112, F, (LPCVOID) o112, F,
(LPCVOID) o113, F, (LPCVOID) o113, F,
(LPCVOID) o114, F, (LPCVOID) o114, F,
(LPCVOID) o_invalid3, F, (LPCVOID) o111, F,
(LPCVOID) o_invalid3, F, (LPCVOID) o112, F,
(LPCVOID) o_invalid3, F, (LPCVOID) o113, F,
(LPCVOID) o118, F, (LPCVOID) o118, F,
(LPCVOID) o119, F, (LPCVOID) o119, F,
(LPCVOID) o11A, F, (LPCVOID) o11A, F,
(LPCVOID) o11B, F, (LPCVOID) o11B, F,
(LPCVOID) o11C, F, (LPCVOID) o11C, F,
(LPCVOID) o_invalid3, F, (LPCVOID) o119, F,
(LPCVOID) o_invalid3, F, (LPCVOID) o11A, F,
(LPCVOID) o_invalid3, F (LPCVOID) o11B, F
}; };
static JMPTAB o10_[] = static JMPTAB o10_[] =
@ -665,17 +665,17 @@ static JMPTAB o10_[] =
(LPCVOID) o102, F, (LPCVOID) o102, F,
(LPCVOID) o103, F, (LPCVOID) o103, F,
(LPCVOID) o104, F, (LPCVOID) o104, F,
(LPCVOID) o_invalid3, F, (LPCVOID) o101, F,
(LPCVOID) o_invalid3, F, (LPCVOID) o102, F,
(LPCVOID) o_invalid3, F, (LPCVOID) o103, F,
(LPCVOID) o108, F, (LPCVOID) o108, F,
(LPCVOID) o109, F, (LPCVOID) o109, F,
(LPCVOID) o10A, F, (LPCVOID) o10A, F,
(LPCVOID) o10B, F, (LPCVOID) o10B, F,
(LPCVOID) o10C, F, (LPCVOID) o10C, F,
(LPCVOID) o_invalid3, F, (LPCVOID) o109, F,
(LPCVOID) o_invalid3, F, (LPCVOID) o10A, F,
(LPCVOID) o_invalid3, F (LPCVOID) o10B, F
}; };
static JMPTAB o1_[] = static JMPTAB o1_[] =

View file

@ -1,7 +1,7 @@
/* /*
LodePNG version 20200306 LodePNG version 20210627
Copyright (c) 2005-2020 Lode Vandevenne Copyright (c) 2005-2021 Lode Vandevenne
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages
@ -44,7 +44,7 @@ Rename this file to lodepng.cpp to use it for C++, or to lodepng.c to use it for
#pragma warning( disable : 4996 ) /*VS does not like fopen, but fopen_s is not standard C so unusable here*/ #pragma warning( disable : 4996 ) /*VS does not like fopen, but fopen_s is not standard C so unusable here*/
#endif /*_MSC_VER */ #endif /*_MSC_VER */
const char* LODEPNG_VERSION_STRING = "20200306"; const char* LODEPNG_VERSION_STRING = "20210627";
/* /*
This source file is built up in the following large parts. The code sections This source file is built up in the following large parts. The code sections
@ -299,6 +299,7 @@ static void string_cleanup(char** out) {
*out = NULL; *out = NULL;
} }
/*also appends null termination character*/
static char* alloc_string_sized(const char* in, size_t insize) { static char* alloc_string_sized(const char* in, size_t insize) {
char* out = (char*)lodepng_malloc(insize + 1); char* out = (char*)lodepng_malloc(insize + 1);
if(out) { if(out) {
@ -1260,7 +1261,7 @@ static unsigned getTreeInflateDynamic(HuffmanTree* tree_ll, HuffmanTree* tree_d,
/*inflate a block with dynamic of fixed Huffman tree. btype must be 1 or 2.*/ /*inflate a block with dynamic of fixed Huffman tree. btype must be 1 or 2.*/
static unsigned inflateHuffmanBlock(ucvector* out, LodePNGBitReader* reader, static unsigned inflateHuffmanBlock(ucvector* out, LodePNGBitReader* reader,
unsigned btype) { unsigned btype, size_t max_output_size) {
unsigned error = 0; unsigned error = 0;
HuffmanTree tree_ll; /*the huffman tree for literal and length codes*/ HuffmanTree tree_ll; /*the huffman tree for literal and length codes*/
HuffmanTree tree_d; /*the huffman tree for distance codes*/ HuffmanTree tree_d; /*the huffman tree for distance codes*/
@ -1341,6 +1342,9 @@ static unsigned inflateHuffmanBlock(ucvector* out, LodePNGBitReader* reader,
/* TODO: revise error codes 10,11,50: the above comment is no longer valid */ /* TODO: revise error codes 10,11,50: the above comment is no longer valid */
ERROR_BREAK(51); /*error, bit pointer jumps past memory*/ ERROR_BREAK(51); /*error, bit pointer jumps past memory*/
} }
if(max_output_size && out->size > max_output_size) {
ERROR_BREAK(109); /*error, larger than max size*/
}
} }
HuffmanTree_cleanup(&tree_ll); HuffmanTree_cleanup(&tree_ll);
@ -1398,9 +1402,9 @@ static unsigned lodepng_inflatev(ucvector* out,
if(BTYPE == 3) return 20; /*error: invalid BTYPE*/ if(BTYPE == 3) return 20; /*error: invalid BTYPE*/
else if(BTYPE == 0) error = inflateNoCompression(out, &reader, settings); /*no compression*/ else if(BTYPE == 0) error = inflateNoCompression(out, &reader, settings); /*no compression*/
else error = inflateHuffmanBlock(out, &reader, BTYPE); /*compression, BTYPE 01 or 10*/ else error = inflateHuffmanBlock(out, &reader, BTYPE, settings->max_output_size); /*compression, BTYPE 01 or 10*/
if(!error && settings->max_output_size && out->size > settings->max_output_size) error = 109;
if(error) return error; if(error) break;
} }
return error; return error;
@ -1421,6 +1425,12 @@ static unsigned inflatev(ucvector* out, const unsigned char* in, size_t insize,
if(settings->custom_inflate) { if(settings->custom_inflate) {
unsigned error = settings->custom_inflate(&out->data, &out->size, in, insize, settings); unsigned error = settings->custom_inflate(&out->data, &out->size, in, insize, settings);
out->allocsize = out->size; out->allocsize = out->size;
if(error) {
/*the custom inflate is allowed to have its own error codes, however, we translate it to code 110*/
error = 110;
/*if there's a max output size, and the custom zlib returned error, then indicate that error instead*/
if(settings->max_output_size && out->size > settings->max_output_size) error = 109;
}
return error; return error;
} else { } else {
return lodepng_inflatev(out, in, insize, settings); return lodepng_inflatev(out, in, insize, settings);
@ -2116,7 +2126,9 @@ static unsigned deflate(unsigned char** out, size_t* outsize,
const unsigned char* in, size_t insize, const unsigned char* in, size_t insize,
const LodePNGCompressSettings* settings) { const LodePNGCompressSettings* settings) {
if(settings->custom_deflate) { if(settings->custom_deflate) {
return settings->custom_deflate(out, outsize, in, insize, settings); unsigned error = settings->custom_deflate(out, outsize, in, insize, settings);
/*the custom deflate is allowed to have its own error codes, however, we translate it to code 111*/
return error ? 111 : 0;
} else { } else {
return lodepng_deflate(out, outsize, in, insize, settings); return lodepng_deflate(out, outsize, in, insize, settings);
} }
@ -2213,10 +2225,16 @@ unsigned lodepng_zlib_decompress(unsigned char** out, size_t* outsize, const uns
/*expected_size is expected output size, to avoid intermediate allocations. Set to 0 if not known. */ /*expected_size is expected output size, to avoid intermediate allocations. Set to 0 if not known. */
static unsigned zlib_decompress(unsigned char** out, size_t* outsize, size_t expected_size, static unsigned zlib_decompress(unsigned char** out, size_t* outsize, size_t expected_size,
const unsigned char* in, size_t insize, const LodePNGDecompressSettings* settings) { const unsigned char* in, size_t insize, const LodePNGDecompressSettings* settings) {
if(settings->custom_zlib) {
return settings->custom_zlib(out, outsize, in, insize, settings);
} else {
unsigned error; unsigned error;
if(settings->custom_zlib) {
error = settings->custom_zlib(out, outsize, in, insize, settings);
if(error) {
/*the custom zlib is allowed to have its own error codes, however, we translate it to code 110*/
error = 110;
/*if there's a max output size, and the custom zlib returned error, then indicate that error instead*/
if(settings->max_output_size && *outsize > settings->max_output_size) error = 109;
}
} else {
ucvector v = ucvector_init(*out, *outsize); ucvector v = ucvector_init(*out, *outsize);
if(expected_size) { if(expected_size) {
/*reserve the memory to avoid intermediate reallocations*/ /*reserve the memory to avoid intermediate reallocations*/
@ -2226,8 +2244,8 @@ static unsigned zlib_decompress(unsigned char** out, size_t* outsize, size_t exp
error = lodepng_zlib_decompressv(&v, in, insize, settings); error = lodepng_zlib_decompressv(&v, in, insize, settings);
*out = v.data; *out = v.data;
*outsize = v.size; *outsize = v.size;
return error;
} }
return error;
} }
#endif /*LODEPNG_COMPILE_DECODER*/ #endif /*LODEPNG_COMPILE_DECODER*/
@ -2275,7 +2293,9 @@ unsigned lodepng_zlib_compress(unsigned char** out, size_t* outsize, const unsig
static unsigned zlib_compress(unsigned char** out, size_t* outsize, const unsigned char* in, static unsigned zlib_compress(unsigned char** out, size_t* outsize, const unsigned char* in,
size_t insize, const LodePNGCompressSettings* settings) { size_t insize, const LodePNGCompressSettings* settings) {
if(settings->custom_zlib) { if(settings->custom_zlib) {
return settings->custom_zlib(out, outsize, in, insize, settings); unsigned error = settings->custom_zlib(out, outsize, in, insize, settings);
/*the custom zlib is allowed to have its own error codes, however, we translate it to code 111*/
return error ? 111 : 0;
} else { } else {
return lodepng_zlib_compress(out, outsize, in, insize, settings); return lodepng_zlib_compress(out, outsize, in, insize, settings);
} }
@ -2334,13 +2354,14 @@ const LodePNGCompressSettings lodepng_default_compress_settings = {2, 1, DEFAULT
void lodepng_decompress_settings_init(LodePNGDecompressSettings* settings) { void lodepng_decompress_settings_init(LodePNGDecompressSettings* settings) {
settings->ignore_adler32 = 0; settings->ignore_adler32 = 0;
settings->ignore_nlen = 0; settings->ignore_nlen = 0;
settings->max_output_size = 0;
settings->custom_zlib = 0; settings->custom_zlib = 0;
settings->custom_inflate = 0; settings->custom_inflate = 0;
settings->custom_context = 0; settings->custom_context = 0;
} }
const LodePNGDecompressSettings lodepng_default_decompress_settings = {0, 0, 0, 0, 0}; const LodePNGDecompressSettings lodepng_default_decompress_settings = {0, 0, 0, 0, 0, 0};
#endif /*LODEPNG_COMPILE_DECODER*/ #endif /*LODEPNG_COMPILE_DECODER*/
@ -2872,8 +2893,8 @@ static void LodePNGText_cleanup(LodePNGInfo* info) {
static unsigned LodePNGText_copy(LodePNGInfo* dest, const LodePNGInfo* source) { static unsigned LodePNGText_copy(LodePNGInfo* dest, const LodePNGInfo* source) {
size_t i = 0; size_t i = 0;
dest->text_keys = 0; dest->text_keys = NULL;
dest->text_strings = 0; dest->text_strings = NULL;
dest->text_num = 0; dest->text_num = 0;
for(i = 0; i != source->text_num; ++i) { for(i = 0; i != source->text_num; ++i) {
CERROR_TRY_RETURN(lodepng_add_text(dest, source->text_keys[i], source->text_strings[i])); CERROR_TRY_RETURN(lodepng_add_text(dest, source->text_keys[i], source->text_strings[i]));
@ -2932,10 +2953,10 @@ static void LodePNGIText_cleanup(LodePNGInfo* info) {
static unsigned LodePNGIText_copy(LodePNGInfo* dest, const LodePNGInfo* source) { static unsigned LodePNGIText_copy(LodePNGInfo* dest, const LodePNGInfo* source) {
size_t i = 0; size_t i = 0;
dest->itext_keys = 0; dest->itext_keys = NULL;
dest->itext_langtags = 0; dest->itext_langtags = NULL;
dest->itext_transkeys = 0; dest->itext_transkeys = NULL;
dest->itext_strings = 0; dest->itext_strings = NULL;
dest->itext_num = 0; dest->itext_num = 0;
for(i = 0; i != source->itext_num; ++i) { for(i = 0; i != source->itext_num; ++i) {
CERROR_TRY_RETURN(lodepng_add_itext(dest, source->itext_keys[i], source->itext_langtags[i], CERROR_TRY_RETURN(lodepng_add_itext(dest, source->itext_keys[i], source->itext_langtags[i],
@ -4093,10 +4114,12 @@ static unsigned unfilterScanline(unsigned char* recon, const unsigned char* scan
case 0: case 0:
for(i = 0; i != length; ++i) recon[i] = scanline[i]; for(i = 0; i != length; ++i) recon[i] = scanline[i];
break; break;
case 1: case 1: {
size_t j = 0;
for(i = 0; i != bytewidth; ++i) recon[i] = scanline[i]; for(i = 0; i != bytewidth; ++i) recon[i] = scanline[i];
for(i = bytewidth; i < length; ++i) recon[i] = scanline[i] + recon[i - bytewidth]; for(i = bytewidth; i != length; ++i, ++j) recon[i] = scanline[i] + recon[j];
break; break;
}
case 2: case 2:
if(precon) { if(precon) {
for(i = 0; i != length; ++i) recon[i] = scanline[i] + precon[i]; for(i = 0; i != length; ++i) recon[i] = scanline[i] + precon[i];
@ -4106,24 +4129,56 @@ static unsigned unfilterScanline(unsigned char* recon, const unsigned char* scan
break; break;
case 3: case 3:
if(precon) { if(precon) {
size_t j = 0;
for(i = 0; i != bytewidth; ++i) recon[i] = scanline[i] + (precon[i] >> 1u); for(i = 0; i != bytewidth; ++i) recon[i] = scanline[i] + (precon[i] >> 1u);
for(i = bytewidth; i < length; ++i) recon[i] = scanline[i] + ((recon[i - bytewidth] + precon[i]) >> 1u); /* Unroll independent paths of this predictor. A 6x and 8x version is also possible but that adds
too much code. Whether this speeds up anything depends on compiler and settings. */
if(bytewidth >= 4) {
for(; i + 3 < length; i += 4, j += 4) {
unsigned char s0 = scanline[i + 0], r0 = recon[j + 0], p0 = precon[i + 0];
unsigned char s1 = scanline[i + 1], r1 = recon[j + 1], p1 = precon[i + 1];
unsigned char s2 = scanline[i + 2], r2 = recon[j + 2], p2 = precon[i + 2];
unsigned char s3 = scanline[i + 3], r3 = recon[j + 3], p3 = precon[i + 3];
recon[i + 0] = s0 + ((r0 + p0) >> 1u);
recon[i + 1] = s1 + ((r1 + p1) >> 1u);
recon[i + 2] = s2 + ((r2 + p2) >> 1u);
recon[i + 3] = s3 + ((r3 + p3) >> 1u);
}
} else if(bytewidth >= 3) {
for(; i + 2 < length; i += 3, j += 3) {
unsigned char s0 = scanline[i + 0], r0 = recon[j + 0], p0 = precon[i + 0];
unsigned char s1 = scanline[i + 1], r1 = recon[j + 1], p1 = precon[i + 1];
unsigned char s2 = scanline[i + 2], r2 = recon[j + 2], p2 = precon[i + 2];
recon[i + 0] = s0 + ((r0 + p0) >> 1u);
recon[i + 1] = s1 + ((r1 + p1) >> 1u);
recon[i + 2] = s2 + ((r2 + p2) >> 1u);
}
} else if(bytewidth >= 2) {
for(; i + 1 < length; i += 2, j += 2) {
unsigned char s0 = scanline[i + 0], r0 = recon[j + 0], p0 = precon[i + 0];
unsigned char s1 = scanline[i + 1], r1 = recon[j + 1], p1 = precon[i + 1];
recon[i + 0] = s0 + ((r0 + p0) >> 1u);
recon[i + 1] = s1 + ((r1 + p1) >> 1u);
}
}
for(; i != length; ++i, ++j) recon[i] = scanline[i] + ((recon[j] + precon[i]) >> 1u);
} else { } else {
size_t j = 0;
for(i = 0; i != bytewidth; ++i) recon[i] = scanline[i]; for(i = 0; i != bytewidth; ++i) recon[i] = scanline[i];
for(i = bytewidth; i < length; ++i) recon[i] = scanline[i] + (recon[i - bytewidth] >> 1u); for(i = bytewidth; i != length; ++i, ++j) recon[i] = scanline[i] + (recon[j] >> 1u);
} }
break; break;
case 4: case 4:
if(precon) { if(precon) {
size_t j = 0;
for(i = 0; i != bytewidth; ++i) { for(i = 0; i != bytewidth; ++i) {
recon[i] = (scanline[i] + precon[i]); /*paethPredictor(0, precon[i], 0) is always precon[i]*/ recon[i] = (scanline[i] + precon[i]); /*paethPredictor(0, precon[i], 0) is always precon[i]*/
} }
/* Unroll independent paths of the paeth predictor. A 6x and 8x version would also be possible but that /* Unroll independent paths of the paeth predictor. A 6x and 8x version is also possible but that
adds too much code. Whether this actually speeds anything up at all depends on compiler and settings. */ adds too much code. Whether this speeds up anything depends on compiler and settings. */
if(bytewidth >= 4) { if(bytewidth >= 4) {
for(; i + 3 < length; i += 4) { for(; i + 3 < length; i += 4, j += 4) {
size_t j = i - bytewidth;
unsigned char s0 = scanline[i + 0], s1 = scanline[i + 1], s2 = scanline[i + 2], s3 = scanline[i + 3]; unsigned char s0 = scanline[i + 0], s1 = scanline[i + 1], s2 = scanline[i + 2], s3 = scanline[i + 3];
unsigned char r0 = recon[j + 0], r1 = recon[j + 1], r2 = recon[j + 2], r3 = recon[j + 3]; unsigned char r0 = recon[j + 0], r1 = recon[j + 1], r2 = recon[j + 2], r3 = recon[j + 3];
unsigned char p0 = precon[i + 0], p1 = precon[i + 1], p2 = precon[i + 2], p3 = precon[i + 3]; unsigned char p0 = precon[i + 0], p1 = precon[i + 1], p2 = precon[i + 2], p3 = precon[i + 3];
@ -4134,8 +4189,7 @@ static unsigned unfilterScanline(unsigned char* recon, const unsigned char* scan
recon[i + 3] = s3 + paethPredictor(r3, p3, q3); recon[i + 3] = s3 + paethPredictor(r3, p3, q3);
} }
} else if(bytewidth >= 3) { } else if(bytewidth >= 3) {
for(; i + 2 < length; i += 3) { for(; i + 2 < length; i += 3, j += 3) {
size_t j = i - bytewidth;
unsigned char s0 = scanline[i + 0], s1 = scanline[i + 1], s2 = scanline[i + 2]; unsigned char s0 = scanline[i + 0], s1 = scanline[i + 1], s2 = scanline[i + 2];
unsigned char r0 = recon[j + 0], r1 = recon[j + 1], r2 = recon[j + 2]; unsigned char r0 = recon[j + 0], r1 = recon[j + 1], r2 = recon[j + 2];
unsigned char p0 = precon[i + 0], p1 = precon[i + 1], p2 = precon[i + 2]; unsigned char p0 = precon[i + 0], p1 = precon[i + 1], p2 = precon[i + 2];
@ -4145,8 +4199,7 @@ static unsigned unfilterScanline(unsigned char* recon, const unsigned char* scan
recon[i + 2] = s2 + paethPredictor(r2, p2, q2); recon[i + 2] = s2 + paethPredictor(r2, p2, q2);
} }
} else if(bytewidth >= 2) { } else if(bytewidth >= 2) {
for(; i + 1 < length; i += 2) { for(; i + 1 < length; i += 2, j += 2) {
size_t j = i - bytewidth;
unsigned char s0 = scanline[i + 0], s1 = scanline[i + 1]; unsigned char s0 = scanline[i + 0], s1 = scanline[i + 1];
unsigned char r0 = recon[j + 0], r1 = recon[j + 1]; unsigned char r0 = recon[j + 0], r1 = recon[j + 1];
unsigned char p0 = precon[i + 0], p1 = precon[i + 1]; unsigned char p0 = precon[i + 0], p1 = precon[i + 1];
@ -4156,16 +4209,17 @@ static unsigned unfilterScanline(unsigned char* recon, const unsigned char* scan
} }
} }
for(; i != length; ++i) { for(; i != length; ++i, ++j) {
recon[i] = (scanline[i] + paethPredictor(recon[i - bytewidth], precon[i], precon[i - bytewidth])); recon[i] = (scanline[i] + paethPredictor(recon[i - bytewidth], precon[i], precon[j]));
} }
} else { } else {
size_t j = 0;
for(i = 0; i != bytewidth; ++i) { for(i = 0; i != bytewidth; ++i) {
recon[i] = scanline[i]; recon[i] = scanline[i];
} }
for(i = bytewidth; i < length; ++i) { for(i = bytewidth; i != length; ++i, ++j) {
/*paethPredictor(recon[i - bytewidth], 0, 0) is always recon[i - bytewidth]*/ /*paethPredictor(recon[i - bytewidth], 0, 0) is always recon[i - bytewidth]*/
recon[i] = (scanline[i] + recon[i - bytewidth]); recon[i] = (scanline[i] + recon[j]);
} }
} }
break; break;
@ -4447,10 +4501,13 @@ static unsigned readChunk_tEXt(LodePNGInfo* info, const unsigned char* data, siz
} }
/*compressed text chunk (zTXt)*/ /*compressed text chunk (zTXt)*/
static unsigned readChunk_zTXt(LodePNGInfo* info, const LodePNGDecompressSettings* zlibsettings, static unsigned readChunk_zTXt(LodePNGInfo* info, const LodePNGDecoderSettings* decoder,
const unsigned char* data, size_t chunkLength) { const unsigned char* data, size_t chunkLength) {
unsigned error = 0; unsigned error = 0;
/*copy the object to change parameters in it*/
LodePNGDecompressSettings zlibsettings = decoder->zlibsettings;
unsigned length, string2_begin; unsigned length, string2_begin;
char *key = 0; char *key = 0;
unsigned char* str = 0; unsigned char* str = 0;
@ -4473,12 +4530,14 @@ static unsigned readChunk_zTXt(LodePNGInfo* info, const LodePNGDecompressSetting
if(string2_begin > chunkLength) CERROR_BREAK(error, 75); /*no null termination, corrupt?*/ if(string2_begin > chunkLength) CERROR_BREAK(error, 75); /*no null termination, corrupt?*/
length = (unsigned)chunkLength - string2_begin; length = (unsigned)chunkLength - string2_begin;
zlibsettings.max_output_size = decoder->max_text_size;
/*will fail if zlib error, e.g. if length is too small*/ /*will fail if zlib error, e.g. if length is too small*/
error = zlib_decompress(&str, &size, 0, &data[string2_begin], error = zlib_decompress(&str, &size, 0, &data[string2_begin],
length, zlibsettings); length, &zlibsettings);
/*error: compressed text larger than decoder->max_text_size*/
if(error && size > zlibsettings.max_output_size) error = 112;
if(error) break; if(error) break;
error = lodepng_add_text_sized(info, key, (char*)str, size); error = lodepng_add_text_sized(info, key, (char*)str, size);
break; break;
} }
@ -4489,11 +4548,14 @@ static unsigned readChunk_zTXt(LodePNGInfo* info, const LodePNGDecompressSetting
} }
/*international text chunk (iTXt)*/ /*international text chunk (iTXt)*/
static unsigned readChunk_iTXt(LodePNGInfo* info, const LodePNGDecompressSettings* zlibsettings, static unsigned readChunk_iTXt(LodePNGInfo* info, const LodePNGDecoderSettings* decoder,
const unsigned char* data, size_t chunkLength) { const unsigned char* data, size_t chunkLength) {
unsigned error = 0; unsigned error = 0;
unsigned i; unsigned i;
/*copy the object to change parameters in it*/
LodePNGDecompressSettings zlibsettings = decoder->zlibsettings;
unsigned length, begin, compressed; unsigned length, begin, compressed;
char *key = 0, *langtag = 0, *transkey = 0; char *key = 0, *langtag = 0, *transkey = 0;
@ -4550,9 +4612,12 @@ static unsigned readChunk_iTXt(LodePNGInfo* info, const LodePNGDecompressSetting
if(compressed) { if(compressed) {
unsigned char* str = 0; unsigned char* str = 0;
size_t size = 0; size_t size = 0;
zlibsettings.max_output_size = decoder->max_text_size;
/*will fail if zlib error, e.g. if length is too small*/ /*will fail if zlib error, e.g. if length is too small*/
error = zlib_decompress(&str, &size, 0, &data[begin], error = zlib_decompress(&str, &size, 0, &data[begin],
length, zlibsettings); length, &zlibsettings);
/*error: compressed text larger than decoder->max_text_size*/
if(error && size > zlibsettings.max_output_size) error = 112;
if(!error) error = lodepng_add_itext_sized(info, key, langtag, transkey, (char*)str, size); if(!error) error = lodepng_add_itext_sized(info, key, langtag, transkey, (char*)str, size);
lodepng_free(str); lodepng_free(str);
} else { } else {
@ -4628,11 +4693,13 @@ static unsigned readChunk_sRGB(LodePNGInfo* info, const unsigned char* data, siz
return 0; /* OK */ return 0; /* OK */
} }
static unsigned readChunk_iCCP(LodePNGInfo* info, const LodePNGDecompressSettings* zlibsettings, static unsigned readChunk_iCCP(LodePNGInfo* info, const LodePNGDecoderSettings* decoder,
const unsigned char* data, size_t chunkLength) { const unsigned char* data, size_t chunkLength) {
unsigned error = 0; unsigned error = 0;
unsigned i; unsigned i;
size_t size = 0; size_t size = 0;
/*copy the object to change parameters in it*/
LodePNGDecompressSettings zlibsettings = decoder->zlibsettings;
unsigned length, string2_begin; unsigned length, string2_begin;
@ -4655,9 +4722,12 @@ static unsigned readChunk_iCCP(LodePNGInfo* info, const LodePNGDecompressSetting
if(string2_begin > chunkLength) return 75; /*no null termination, corrupt?*/ if(string2_begin > chunkLength) return 75; /*no null termination, corrupt?*/
length = (unsigned)chunkLength - string2_begin; length = (unsigned)chunkLength - string2_begin;
zlibsettings.max_output_size = decoder->max_icc_size;
error = zlib_decompress(&info->iccp_profile, &size, 0, error = zlib_decompress(&info->iccp_profile, &size, 0,
&data[string2_begin], &data[string2_begin],
length, zlibsettings); length, &zlibsettings);
/*error: ICC profile larger than decoder->max_icc_size*/
if(error && size > zlibsettings.max_output_size) error = 113;
info->iccp_profile_size = (unsigned)size; info->iccp_profile_size = (unsigned)size;
if(!error && !info->iccp_profile_size) error = 100; /*invalid ICC profile size*/ if(!error && !info->iccp_profile_size) error = 100; /*invalid ICC profile size*/
return error; return error;
@ -4688,9 +4758,9 @@ unsigned lodepng_inspect_chunk(LodePNGState* state, size_t pos,
} else if(lodepng_chunk_type_equals(chunk, "tEXt")) { } else if(lodepng_chunk_type_equals(chunk, "tEXt")) {
error = readChunk_tEXt(&state->info_png, data, chunkLength); error = readChunk_tEXt(&state->info_png, data, chunkLength);
} else if(lodepng_chunk_type_equals(chunk, "zTXt")) { } else if(lodepng_chunk_type_equals(chunk, "zTXt")) {
error = readChunk_zTXt(&state->info_png, &state->decoder.zlibsettings, data, chunkLength); error = readChunk_zTXt(&state->info_png, &state->decoder, data, chunkLength);
} else if(lodepng_chunk_type_equals(chunk, "iTXt")) { } else if(lodepng_chunk_type_equals(chunk, "iTXt")) {
error = readChunk_iTXt(&state->info_png, &state->decoder.zlibsettings, data, chunkLength); error = readChunk_iTXt(&state->info_png, &state->decoder, data, chunkLength);
} else if(lodepng_chunk_type_equals(chunk, "tIME")) { } else if(lodepng_chunk_type_equals(chunk, "tIME")) {
error = readChunk_tIME(&state->info_png, data, chunkLength); error = readChunk_tIME(&state->info_png, data, chunkLength);
} else if(lodepng_chunk_type_equals(chunk, "pHYs")) { } else if(lodepng_chunk_type_equals(chunk, "pHYs")) {
@ -4702,7 +4772,7 @@ unsigned lodepng_inspect_chunk(LodePNGState* state, size_t pos,
} else if(lodepng_chunk_type_equals(chunk, "sRGB")) { } else if(lodepng_chunk_type_equals(chunk, "sRGB")) {
error = readChunk_sRGB(&state->info_png, data, chunkLength); error = readChunk_sRGB(&state->info_png, data, chunkLength);
} else if(lodepng_chunk_type_equals(chunk, "iCCP")) { } else if(lodepng_chunk_type_equals(chunk, "iCCP")) {
error = readChunk_iCCP(&state->info_png, &state->decoder.zlibsettings, data, chunkLength); error = readChunk_iCCP(&state->info_png, &state->decoder, data, chunkLength);
#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
} else { } else {
/* unhandled chunk is ok (is not an error) */ /* unhandled chunk is ok (is not an error) */
@ -4820,13 +4890,13 @@ static void decodeGeneric(unsigned char** out, unsigned* w, unsigned* h,
} else if(lodepng_chunk_type_equals(chunk, "zTXt")) { } else if(lodepng_chunk_type_equals(chunk, "zTXt")) {
/*compressed text chunk (zTXt)*/ /*compressed text chunk (zTXt)*/
if(state->decoder.read_text_chunks) { if(state->decoder.read_text_chunks) {
state->error = readChunk_zTXt(&state->info_png, &state->decoder.zlibsettings, data, chunkLength); state->error = readChunk_zTXt(&state->info_png, &state->decoder, data, chunkLength);
if(state->error) break; if(state->error) break;
} }
} else if(lodepng_chunk_type_equals(chunk, "iTXt")) { } else if(lodepng_chunk_type_equals(chunk, "iTXt")) {
/*international text chunk (iTXt)*/ /*international text chunk (iTXt)*/
if(state->decoder.read_text_chunks) { if(state->decoder.read_text_chunks) {
state->error = readChunk_iTXt(&state->info_png, &state->decoder.zlibsettings, data, chunkLength); state->error = readChunk_iTXt(&state->info_png, &state->decoder, data, chunkLength);
if(state->error) break; if(state->error) break;
} }
} else if(lodepng_chunk_type_equals(chunk, "tIME")) { } else if(lodepng_chunk_type_equals(chunk, "tIME")) {
@ -4845,7 +4915,7 @@ static void decodeGeneric(unsigned char** out, unsigned* w, unsigned* h,
state->error = readChunk_sRGB(&state->info_png, data, chunkLength); state->error = readChunk_sRGB(&state->info_png, data, chunkLength);
if(state->error) break; if(state->error) break;
} else if(lodepng_chunk_type_equals(chunk, "iCCP")) { } else if(lodepng_chunk_type_equals(chunk, "iCCP")) {
state->error = readChunk_iCCP(&state->info_png, &state->decoder.zlibsettings, data, chunkLength); state->error = readChunk_iCCP(&state->info_png, &state->decoder, data, chunkLength);
if(state->error) break; if(state->error) break;
#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
} else /*it's not an implemented chunk type, so ignore it: skip over the data*/ { } else /*it's not an implemented chunk type, so ignore it: skip over the data*/ {
@ -4871,7 +4941,7 @@ static void decodeGeneric(unsigned char** out, unsigned* w, unsigned* h,
if(!IEND) chunk = lodepng_chunk_next_const(chunk, in + insize); if(!IEND) chunk = lodepng_chunk_next_const(chunk, in + insize);
} }
if(state->info_png.color.colortype == LCT_PALETTE && !state->info_png.color.palette) { if(!state->error && state->info_png.color.colortype == LCT_PALETTE && !state->info_png.color.palette) {
state->error = 106; /* error: PNG file must have PLTE chunk if color type is palette */ state->error = 106; /* error: PNG file must have PLTE chunk if color type is palette */
} }
@ -4955,6 +5025,11 @@ unsigned lodepng_decode_memory(unsigned char** out, unsigned* w, unsigned* h, co
lodepng_state_init(&state); lodepng_state_init(&state);
state.info_raw.colortype = colortype; state.info_raw.colortype = colortype;
state.info_raw.bitdepth = bitdepth; state.info_raw.bitdepth = bitdepth;
#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
/*disable reading things that this function doesn't output*/
state.decoder.read_text_chunks = 0;
state.decoder.remember_unknown_chunks = 0;
#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
error = lodepng_decode(out, w, h, &state, in, insize); error = lodepng_decode(out, w, h, &state, in, insize);
lodepng_state_cleanup(&state); lodepng_state_cleanup(&state);
return error; return error;
@ -4997,6 +5072,8 @@ void lodepng_decoder_settings_init(LodePNGDecoderSettings* settings) {
#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
settings->read_text_chunks = 1; settings->read_text_chunks = 1;
settings->remember_unknown_chunks = 0; settings->remember_unknown_chunks = 0;
settings->max_text_size = 16777216;
settings->max_icc_size = 16777216; /* 16MB is much more than enough for any reasonable ICC profile */
#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
settings->ignore_crc = 0; settings->ignore_crc = 0;
settings->ignore_critical = 0; settings->ignore_critical = 0;
@ -6204,6 +6281,16 @@ const char* lodepng_error_text(unsigned code) {
case 106: return "PNG file must have PLTE chunk if color type is palette"; case 106: return "PNG file must have PLTE chunk if color type is palette";
case 107: return "color convert from palette mode requested without setting the palette data in it"; case 107: return "color convert from palette mode requested without setting the palette data in it";
case 108: return "tried to add more than 256 values to a palette"; case 108: return "tried to add more than 256 values to a palette";
/*this limit can be configured in LodePNGDecompressSettings*/
case 109: return "tried to decompress zlib or deflate data larger than desired max_output_size";
case 110: return "custom zlib or inflate decompression failed";
case 111: return "custom zlib or deflate compression failed";
/*max text size limit can be configured in LodePNGDecoderSettings. This error prevents
unreasonable memory consumption when decoding due to impossibly large text sizes.*/
case 112: return "compressed text unreasonably large";
/*max ICC size limit can be configured in LodePNGDecoderSettings. This error prevents
unreasonable memory consumption when decoding due to impossibly large ICC profile*/
case 113: return "ICC profile unreasonably large";
} }
return "unknown error code"; return "unknown error code";
} }

View file

@ -1,7 +1,7 @@
/* /*
LodePNG version 20200306 LodePNG version 20210627
Copyright (c) 2005-2020 Lode Vandevenne Copyright (c) 2005-2021 Lode Vandevenne
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages
@ -142,16 +142,24 @@ unsigned lodepng_decode24(unsigned char** out, unsigned* w, unsigned* h,
/* /*
Load PNG from disk, from file with given name. Load PNG from disk, from file with given name.
Same as the other decode functions, but instead takes a filename as input. Same as the other decode functions, but instead takes a filename as input.
*/
NOTE: Wide-character filenames are not supported, you can use an external method
to handle such files and decode in-memory.*/
unsigned lodepng_decode_file(unsigned char** out, unsigned* w, unsigned* h, unsigned lodepng_decode_file(unsigned char** out, unsigned* w, unsigned* h,
const char* filename, const char* filename,
LodePNGColorType colortype, unsigned bitdepth); LodePNGColorType colortype, unsigned bitdepth);
/*Same as lodepng_decode_file, but always decodes to 32-bit RGBA raw image.*/ /*Same as lodepng_decode_file, but always decodes to 32-bit RGBA raw image.
NOTE: Wide-character filenames are not supported, you can use an external method
to handle such files and decode in-memory.*/
unsigned lodepng_decode32_file(unsigned char** out, unsigned* w, unsigned* h, unsigned lodepng_decode32_file(unsigned char** out, unsigned* w, unsigned* h,
const char* filename); const char* filename);
/*Same as lodepng_decode_file, but always decodes to 24-bit RGB raw image.*/ /*Same as lodepng_decode_file, but always decodes to 24-bit RGB raw image.
NOTE: Wide-character filenames are not supported, you can use an external method
to handle such files and decode in-memory.*/
unsigned lodepng_decode24_file(unsigned char** out, unsigned* w, unsigned* h, unsigned lodepng_decode24_file(unsigned char** out, unsigned* w, unsigned* h,
const char* filename); const char* filename);
#endif /*LODEPNG_COMPILE_DISK*/ #endif /*LODEPNG_COMPILE_DISK*/
@ -191,17 +199,26 @@ unsigned lodepng_encode24(unsigned char** out, size_t* outsize,
/* /*
Converts raw pixel data into a PNG file on disk. Converts raw pixel data into a PNG file on disk.
Same as the other encode functions, but instead takes a filename as output. Same as the other encode functions, but instead takes a filename as output.
NOTE: This overwrites existing files without warning! NOTE: This overwrites existing files without warning!
*/
NOTE: Wide-character filenames are not supported, you can use an external method
to handle such files and encode in-memory.*/
unsigned lodepng_encode_file(const char* filename, unsigned lodepng_encode_file(const char* filename,
const unsigned char* image, unsigned w, unsigned h, const unsigned char* image, unsigned w, unsigned h,
LodePNGColorType colortype, unsigned bitdepth); LodePNGColorType colortype, unsigned bitdepth);
/*Same as lodepng_encode_file, but always encodes from 32-bit RGBA raw image.*/ /*Same as lodepng_encode_file, but always encodes from 32-bit RGBA raw image.
NOTE: Wide-character filenames are not supported, you can use an external method
to handle such files and encode in-memory.*/
unsigned lodepng_encode32_file(const char* filename, unsigned lodepng_encode32_file(const char* filename,
const unsigned char* image, unsigned w, unsigned h); const unsigned char* image, unsigned w, unsigned h);
/*Same as lodepng_encode_file, but always encodes from 24-bit RGB raw image.*/ /*Same as lodepng_encode_file, but always encodes from 24-bit RGB raw image.
NOTE: Wide-character filenames are not supported, you can use an external method
to handle such files and encode in-memory.*/
unsigned lodepng_encode24_file(const char* filename, unsigned lodepng_encode24_file(const char* filename,
const unsigned char* image, unsigned w, unsigned h); const unsigned char* image, unsigned w, unsigned h);
#endif /*LODEPNG_COMPILE_DISK*/ #endif /*LODEPNG_COMPILE_DISK*/
@ -223,6 +240,9 @@ unsigned decode(std::vector<unsigned char>& out, unsigned& w, unsigned& h,
/* /*
Converts PNG file from disk to raw pixel data in memory. Converts PNG file from disk to raw pixel data in memory.
Same as the other decode functions, but instead takes a filename as input. Same as the other decode functions, but instead takes a filename as input.
NOTE: Wide-character filenames are not supported, you can use an external method
to handle such files and decode in-memory.
*/ */
unsigned decode(std::vector<unsigned char>& out, unsigned& w, unsigned& h, unsigned decode(std::vector<unsigned char>& out, unsigned& w, unsigned& h,
const std::string& filename, const std::string& filename,
@ -243,7 +263,11 @@ unsigned encode(std::vector<unsigned char>& out,
/* /*
Converts 32-bit RGBA raw pixel data into a PNG file on disk. Converts 32-bit RGBA raw pixel data into a PNG file on disk.
Same as the other encode functions, but instead takes a filename as output. Same as the other encode functions, but instead takes a filename as output.
NOTE: This overwrites existing files without warning! NOTE: This overwrites existing files without warning!
NOTE: Wide-character filenames are not supported, you can use an external method
to handle such files and decode in-memory.
*/ */
unsigned encode(const std::string& filename, unsigned encode(const std::string& filename,
const unsigned char* in, unsigned w, unsigned h, const unsigned char* in, unsigned w, unsigned h,
@ -270,12 +294,21 @@ struct LodePNGDecompressSettings {
unsigned ignore_adler32; /*if 1, continue and don't give an error message if the Adler32 checksum is corrupted*/ unsigned ignore_adler32; /*if 1, continue and don't give an error message if the Adler32 checksum is corrupted*/
unsigned ignore_nlen; /*ignore complement of len checksum in uncompressed blocks*/ unsigned ignore_nlen; /*ignore complement of len checksum in uncompressed blocks*/
/*use custom zlib decoder instead of built in one (default: null)*/ /*Maximum decompressed size, beyond this the decoder may (and is encouraged to) stop decoding,
return an error, output a data size > max_output_size and all the data up to that point. This is
not hard limit nor a guarantee, but can prevent excessive memory usage. This setting is
ignored by the PNG decoder, but is used by the deflate/zlib decoder and can be used by custom ones.
Set to 0 to impose no limit (the default).*/
size_t max_output_size;
/*use custom zlib decoder instead of built in one (default: null).
Should return 0 if success, any non-0 if error (numeric value not exposed).*/
unsigned (*custom_zlib)(unsigned char**, size_t*, unsigned (*custom_zlib)(unsigned char**, size_t*,
const unsigned char*, size_t, const unsigned char*, size_t,
const LodePNGDecompressSettings*); const LodePNGDecompressSettings*);
/*use custom deflate decoder instead of built in one (default: null) /*use custom deflate decoder instead of built in one (default: null)
if custom_zlib is not null, custom_inflate is ignored (the zlib format uses deflate)*/ if custom_zlib is not null, custom_inflate is ignored (the zlib format uses deflate).
Should return 0 if success, any non-0 if error (numeric value not exposed).*/
unsigned (*custom_inflate)(unsigned char**, size_t*, unsigned (*custom_inflate)(unsigned char**, size_t*,
const unsigned char*, size_t, const unsigned char*, size_t,
const LodePNGDecompressSettings*); const LodePNGDecompressSettings*);
@ -454,30 +487,36 @@ typedef struct LodePNGInfo {
unsigned background_b; /*blue component of suggested background color*/ unsigned background_b; /*blue component of suggested background color*/
/* /*
non-international text chunks (tEXt and zTXt) Non-international text chunks (tEXt and zTXt)
The char** arrays each contain num strings. The actual messages are in The char** arrays each contain num strings. The actual messages are in
text_strings, while text_keys are keywords that give a short description what text_strings, while text_keys are keywords that give a short description what
the actual text represents, e.g. Title, Author, Description, or anything else. the actual text represents, e.g. Title, Author, Description, or anything else.
All the string fields below including keys, names and language tags are null terminated. All the string fields below including strings, keys, names and language tags are null terminated.
The PNG specification uses null characters for the keys, names and tags, and forbids null The PNG specification uses null characters for the keys, names and tags, and forbids null
characters to appear in the main text which is why we can use null termination everywhere here. characters to appear in the main text which is why we can use null termination everywhere here.
A keyword is minimum 1 character and maximum 79 characters long. It's A keyword is minimum 1 character and maximum 79 characters long (plus the
discouraged to use a single line length longer than 79 characters for texts. additional null terminator). It's discouraged to use a single line length
longer than 79 characters for texts.
Don't allocate these text buffers yourself. Use the init/cleanup functions Don't allocate these text buffers yourself. Use the init/cleanup functions
correctly and use lodepng_add_text and lodepng_clear_text. correctly and use lodepng_add_text and lodepng_clear_text.
Standard text chunk keywords and strings are encoded using Latin-1.
*/ */
size_t text_num; /*the amount of texts in these char** buffers (there may be more texts in itext)*/ size_t text_num; /*the amount of texts in these char** buffers (there may be more texts in itext)*/
char** text_keys; /*the keyword of a text chunk (e.g. "Comment")*/ char** text_keys; /*the keyword of a text chunk (e.g. "Comment")*/
char** text_strings; /*the actual text*/ char** text_strings; /*the actual text*/
/* /*
international text chunks (iTXt) International text chunks (iTXt)
Similar to the non-international text chunks, but with additional strings Similar to the non-international text chunks, but with additional strings
"langtags" and "transkeys". "langtags" and "transkeys", and the following text encodings are used:
keys: Latin-1, langtags: ASCII, transkeys and strings: UTF-8.
keys must be 1-79 characters (plus the additional null terminator), the other
strings are any length.
*/ */
size_t itext_num; /*the amount of international texts in this PNG*/ size_t itext_num; /*the amount of international texts in this PNG*/
char** itext_keys; /*the English keyword of the text chunk (e.g. "Comment")*/ char** itext_keys; /*the English keyword of the text chunk (e.g. "Comment")*/
@ -639,8 +678,19 @@ typedef struct LodePNGDecoderSettings {
#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
unsigned read_text_chunks; /*if false but remember_unknown_chunks is true, they're stored in the unknown chunks*/ unsigned read_text_chunks; /*if false but remember_unknown_chunks is true, they're stored in the unknown chunks*/
/*store all bytes from unknown chunks in the LodePNGInfo (off by default, useful for a png editor)*/ /*store all bytes from unknown chunks in the LodePNGInfo (off by default, useful for a png editor)*/
unsigned remember_unknown_chunks; unsigned remember_unknown_chunks;
/* maximum size for decompressed text chunks. If a text chunk's text is larger than this, an error is returned,
unless reading text chunks is disabled or this limit is set higher or disabled. Set to 0 to allow any size.
By default it is a value that prevents unreasonably large strings from hogging memory. */
size_t max_text_size;
/* maximum size for compressed ICC chunks. If the ICC profile is larger than this, an error will be returned. Set to
0 to allow any size. By default this is a value that prevents ICC profiles that would be much larger than any
legitimate profile could be to hog memory. */
size_t max_icc_size;
#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
} LodePNGDecoderSettings; } LodePNGDecoderSettings;
@ -950,6 +1000,9 @@ out: output parameter, contains pointer to loaded buffer.
outsize: output parameter, size of the allocated out buffer outsize: output parameter, size of the allocated out buffer
filename: the path to the file to load filename: the path to the file to load
return value: error code (0 means ok) return value: error code (0 means ok)
NOTE: Wide-character filenames are not supported, you can use an external method
to handle such files and decode in-memory.
*/ */
unsigned lodepng_load_file(unsigned char** out, size_t* outsize, const char* filename); unsigned lodepng_load_file(unsigned char** out, size_t* outsize, const char* filename);
@ -960,6 +1013,9 @@ buffer: the buffer to write
buffersize: size of the buffer to write buffersize: size of the buffer to write
filename: the path to the file to save to filename: the path to the file to save to
return value: error code (0 means ok) return value: error code (0 means ok)
NOTE: Wide-character filenames are not supported, you can use an external method
to handle such files and encode in-memory
*/ */
unsigned lodepng_save_file(const unsigned char* buffer, size_t buffersize, const char* filename); unsigned lodepng_save_file(const unsigned char* buffer, size_t buffersize, const char* filename);
#endif /*LODEPNG_COMPILE_DISK*/ #endif /*LODEPNG_COMPILE_DISK*/
@ -1000,12 +1056,18 @@ unsigned encode(std::vector<unsigned char>& out,
/* /*
Load a file from disk into an std::vector. Load a file from disk into an std::vector.
return value: error code (0 means ok) return value: error code (0 means ok)
NOTE: Wide-character filenames are not supported, you can use an external method
to handle such files and decode in-memory
*/ */
unsigned load_file(std::vector<unsigned char>& buffer, const std::string& filename); unsigned load_file(std::vector<unsigned char>& buffer, const std::string& filename);
/* /*
Save the binary data in an std::vector to a file on disk. The file is overwritten Save the binary data in an std::vector to a file on disk. The file is overwritten
without warning. without warning.
NOTE: Wide-character filenames are not supported, you can use an external method
to handle such files and encode in-memory
*/ */
unsigned save_file(const std::vector<unsigned char>& buffer, const std::string& filename); unsigned save_file(const std::vector<unsigned char>& buffer, const std::string& filename);
#endif /* LODEPNG_COMPILE_DISK */ #endif /* LODEPNG_COMPILE_DISK */
@ -1505,6 +1567,11 @@ of the error in English as a string.
Check the implementation of lodepng_error_text to see the meaning of each code. Check the implementation of lodepng_error_text to see the meaning of each code.
It is not recommended to use the numerical values to programmatically make
different decisions based on error types as the numbers are not guaranteed to
stay backwards compatible. They are for human consumption only. Programmatically
only 0 or non-0 matter.
8. chunks and PNG editing 8. chunks and PNG editing
------------------------- -------------------------
@ -1678,6 +1745,9 @@ try to fix it if the compiler is modern and standards compliant.
This decoder example shows the most basic usage of LodePNG. More complex This decoder example shows the most basic usage of LodePNG. More complex
examples can be found on the LodePNG website. examples can be found on the LodePNG website.
NOTE: these examples do not support wide-character filenames, you can use an
external method to handle such files and encode or decode in-memory
10.1. decoder C++ example 10.1. decoder C++ example
------------------------- -------------------------
@ -1775,6 +1845,10 @@ symbol.
Not all changes are listed here, the commit history in github lists more: Not all changes are listed here, the commit history in github lists more:
https://github.com/lvandeve/lodepng https://github.com/lvandeve/lodepng
*) 27 jun 2021: added warnings that file reading/writing functions don't support
wide-character filenames (support for this is not planned, opening files is
not the core part of PNG decoding/decoding and is platform dependent).
*) 17 okt 2020: prevent decoding too large text/icc chunks by default.
*) 06 mar 2020: simplified some of the dynamic memory allocations. *) 06 mar 2020: simplified some of the dynamic memory allocations.
*) 12 jan 2020: (!) added 'end' argument to lodepng_chunk_next to allow correct *) 12 jan 2020: (!) added 'end' argument to lodepng_chunk_next to allow correct
overflow checks. overflow checks.
@ -1941,5 +2015,5 @@ Domain: gmail dot com.
Account: lode dot vandevenne. Account: lode dot vandevenne.
Copyright (c) 2005-2020 Lode Vandevenne Copyright (c) 2005-2021 Lode Vandevenne
*/ */

View file

@ -1009,9 +1009,9 @@ static DWORD ReadT2Acc(VOID)
// maybe CPU speed measurement, slow down the next 10 CPU opcodes // maybe CPU speed measurement, slow down the next 10 CPU opcodes
if (dwCycDif < 150) if (dwCycDif < 150)
{ {
InitAdjustSpeed(); // init variables if necessary
EnterCriticalSection(&csSlowLock); EnterCriticalSection(&csSlowLock);
{ {
InitAdjustSpeed(); // init variables if necessary
nOpcSlow = 10; // slow down next 10 opcodes nOpcSlow = 10; // slow down next 10 opcodes
} }
LeaveCriticalSection(&csSlowLock); LeaveCriticalSection(&csSlowLock);

View file

@ -504,22 +504,14 @@ VOID SoundOut(CHIPSET* w, WORD wOut)
} }
// ran out of buffers -> disable CPU slow down // ran out of buffers -> disable CPU slow down
EnterCriticalSection(&csSlowLock);
{
InitAdjustSpeed(); // init variables if necessary InitAdjustSpeed(); // init variables if necessary
bEnableSlow = (GetSoundBufSize() > 1); bEnableSlow = (GetSoundBufSize() > 1);
}
LeaveCriticalSection(&csSlowLock);
if (bSoundSlow == FALSE) if (bSoundSlow == FALSE)
{
EnterCriticalSection(&csSlowLock);
{ {
InitAdjustSpeed(); // init variables if necessary InitAdjustSpeed(); // init variables if necessary
bSoundSlow = TRUE; // CPU slow down bSoundSlow = TRUE; // CPU slow down
} }
LeaveCriticalSection(&csSlowLock);
}
return; return;
} }

View file

@ -7,7 +7,7 @@ buildscript {
jcenter() jcenter()
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:7.0.2' classpath 'com.android.tools.build:gradle:7.0.4'
// NOTE: Do not place your application dependencies here; they belong // NOTE: Do not place your application dependencies here; they belong