mirror of
https://github.com/RaySollium99/picodrive.git
synced 2025-09-05 07:17:45 -04:00
experimental thread I/O code, fps counter fix
git-svn-id: file:///home/notaz/opt/svn/PicoDrive@396 be3aeb3a-fb24-0410-a615-afba39da0efa
This commit is contained in:
parent
7b802576b2
commit
da31028324
5 changed files with 251 additions and 103 deletions
|
@ -9,6 +9,125 @@ static int prev_lba = 0x80000000;
|
|||
|
||||
static int hits, reads;
|
||||
|
||||
//#define THREADED_CD_IO
|
||||
|
||||
/* threaded reader */
|
||||
#ifdef THREADED_CD_IO
|
||||
#include <pthread.h>
|
||||
#define tioprintf printf
|
||||
|
||||
static pthread_t thr_thread = 0;
|
||||
static pthread_cond_t thr_cond = PTHREAD_COND_INITIALIZER;
|
||||
static pthread_mutex_t thr_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
static unsigned char *thr_buffer[2][2048 + 304] __attribute__((aligned(4)));
|
||||
static int thr_lba_need;
|
||||
static int thr_lba_have[2];
|
||||
|
||||
static void thr_read_lba(int slot, int lba)
|
||||
{
|
||||
int is_bin = Pico_mcd->TOC.Tracks[0].ftype == TYPE_BIN;
|
||||
int where_seek = is_bin ? (lba * 2352 + 16) : (lba << 11);
|
||||
|
||||
pm_seek(Pico_mcd->TOC.Tracks[0].F, where_seek, SEEK_SET);
|
||||
pm_read(thr_buffer[slot], 2048, Pico_mcd->TOC.Tracks[0].F);
|
||||
thr_lba_have[slot] = lba;
|
||||
}
|
||||
|
||||
static void *buffering_thread(void *arg)
|
||||
{
|
||||
int free_slot, lba;
|
||||
|
||||
elprintf(EL_STATUS, "CD I/O thread started.");
|
||||
|
||||
pthread_mutex_lock(&thr_mutex);
|
||||
|
||||
while (1)
|
||||
{
|
||||
if (thr_lba_need < 0) goto wait;
|
||||
|
||||
free_slot = -1;
|
||||
if (thr_lba_have[0] == -1) free_slot = 0;
|
||||
if (thr_lba_have[1] == -1) free_slot = 1;
|
||||
if (free_slot == -1) goto wait;
|
||||
|
||||
lba = thr_lba_need;
|
||||
if (lba != thr_lba_have[free_slot^1]) {
|
||||
thr_read_lba(free_slot, lba);
|
||||
tioprintf("t done %i %i\n", lba, free_slot);
|
||||
continue;
|
||||
}
|
||||
lba++;
|
||||
if (lba != thr_lba_have[free_slot^1]) {
|
||||
thr_read_lba(free_slot, lba);
|
||||
tioprintf("t done %i %i\n", lba, free_slot);
|
||||
continue;
|
||||
}
|
||||
|
||||
wait:
|
||||
pthread_cond_wait(&thr_cond, &thr_mutex);
|
||||
tioprintf("t wake\n");
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&thr_mutex);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void threaded_read(void *dest, int lba)
|
||||
{
|
||||
int i, have = -1;
|
||||
tioprintf("\n");
|
||||
|
||||
if (lba == thr_lba_have[0]) have = 0;
|
||||
if (lba == thr_lba_have[1]) have = 1;
|
||||
if (have != -1)
|
||||
{
|
||||
tioprintf("r hit %i %i\n", lba, have);
|
||||
memcpy32(dest, (int *)thr_buffer[have], 2048/4);
|
||||
if (lba != prev_lba) {
|
||||
thr_lba_have[have] = -1; // make free slot
|
||||
thr_lba_need = lba + 1; // guess a sequential read..
|
||||
pthread_cond_signal(&thr_cond);
|
||||
sched_yield();
|
||||
prev_lba = lba;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
tioprintf("r miss %i\n", lba);
|
||||
thr_lba_need = lba;
|
||||
pthread_mutex_lock(&thr_mutex);
|
||||
pthread_mutex_unlock(&thr_mutex);
|
||||
if (lba == thr_lba_have[0]) have = 0;
|
||||
if (lba == thr_lba_have[1]) have = 1;
|
||||
if (have == -1)
|
||||
{
|
||||
// try again..
|
||||
thr_lba_have[0] = thr_lba_have[1] = -1;
|
||||
for (i = 0; have == -1 && i < 10; i++)
|
||||
{
|
||||
tioprintf("r hard %i\n", lba);
|
||||
pthread_cond_signal(&thr_cond);
|
||||
sched_yield();
|
||||
pthread_mutex_lock(&thr_mutex);
|
||||
pthread_mutex_unlock(&thr_mutex);
|
||||
if (lba == thr_lba_have[0]) have = 0;
|
||||
if (lba == thr_lba_have[1]) have = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// we MUST have the data at this point..
|
||||
if (have == -1) { tioprintf("BUG!\n"); exit(1); }
|
||||
tioprintf("r reco %i %i\n", lba, have);
|
||||
memcpy32(dest, (int *)thr_buffer[have], 2048/4);
|
||||
thr_lba_have[have] = -1;
|
||||
pthread_cond_signal(&thr_cond);
|
||||
|
||||
prev_lba = lba;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
void PicoCDBufferInit(void)
|
||||
{
|
||||
|
@ -16,10 +135,11 @@ void PicoCDBufferInit(void)
|
|||
|
||||
prev_lba = 0x80000000;
|
||||
hits = reads = 0;
|
||||
cd_buffer = NULL;
|
||||
|
||||
if (PicoCDBuffers <= 1) {
|
||||
PicoCDBuffers = 0;
|
||||
return; /* buffering off */
|
||||
goto no_buffering; /* buffering off */
|
||||
}
|
||||
|
||||
/* try alloc'ing until we succeed */
|
||||
|
@ -30,14 +150,28 @@ void PicoCDBufferInit(void)
|
|||
PicoCDBuffers >>= 1;
|
||||
}
|
||||
|
||||
if (PicoCDBuffers <= 0) return; /* buffering became off */
|
||||
if (PicoCDBuffers > 0) {
|
||||
cd_buffer = tmp;
|
||||
return;
|
||||
}
|
||||
|
||||
cd_buffer = tmp;
|
||||
no_buffering:;
|
||||
#ifdef THREADED_CD_IO
|
||||
thr_lba_need = thr_lba_have[0] = thr_lba_have[1] = -1;
|
||||
if (thr_thread == 0)
|
||||
{
|
||||
pthread_create(&thr_thread, NULL, buffering_thread, NULL);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void PicoCDBufferFree(void)
|
||||
{
|
||||
#ifdef THREADED_CD_IO
|
||||
pthread_mutex_lock(&thr_mutex);
|
||||
pthread_mutex_unlock(&thr_mutex);
|
||||
#endif
|
||||
if (cd_buffer) {
|
||||
free(cd_buffer);
|
||||
cd_buffer = NULL;
|
||||
|
@ -57,11 +191,16 @@ PICO_INTERNAL void PicoCDBufferRead(void *dest, int lba)
|
|||
|
||||
if (PicoCDBuffers <= 0)
|
||||
{
|
||||
#ifdef THREADED_CD_IO
|
||||
threaded_read(dest, lba);
|
||||
return;
|
||||
#else
|
||||
/* no buffering */
|
||||
int where_seek = is_bin ? (lba * 2352 + 16) : (lba << 11);
|
||||
pm_seek(Pico_mcd->TOC.Tracks[0].F, where_seek, SEEK_SET);
|
||||
pm_read(dest, 2048, Pico_mcd->TOC.Tracks[0].F);
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* hit? */
|
||||
|
|
|
@ -643,6 +643,8 @@ Changelog
|
|||
configs are now held in single file (but old game config files are still
|
||||
read).
|
||||
* Fixed a bug where some key combos didn't work.
|
||||
* Fixed a regression in renderer (rare graphic glitches).
|
||||
* Adjusted fast rernderer to work with more games, including VR.
|
||||
|
||||
1.35b
|
||||
* PSP: mp3 code should no longer fail on 1.5 firmware.
|
||||
|
|
|
@ -458,10 +458,10 @@ static void updateKeys(void)
|
|||
pl = (acts >> 16) & 1;
|
||||
if (kb_combo_keys & (1 << i))
|
||||
{
|
||||
int u, acts_c = acts & kb_combo_acts;
|
||||
int u = i+1, acts_c = acts & kb_combo_acts;
|
||||
// let's try to find the other one
|
||||
if (acts_c) {
|
||||
for (u = i + 1; u < 32; u++)
|
||||
for (; u < 32; u++)
|
||||
if ( (keys & (1 << u)) && (currentConfig.KeyBinds[u] & acts_c) ) {
|
||||
allActions[pl] |= acts_c & currentConfig.KeyBinds[u];
|
||||
keys &= ~((1 << i) | (1 << u));
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include <linux/limits.h>
|
||||
#include <ctype.h>
|
||||
#include <unistd.h>
|
||||
#include <sched.h>
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
|
@ -541,10 +542,10 @@ static void updateKeys(void)
|
|||
pl = (acts >> 16) & 1;
|
||||
if (kb_combo_keys & (1 << i))
|
||||
{
|
||||
int u, acts_c = acts & kb_combo_acts;
|
||||
int u = i+1, acts_c = acts & kb_combo_acts;
|
||||
// let's try to find the other one
|
||||
if (acts_c) {
|
||||
for (u = i + 1; u < 32; u++)
|
||||
for (; u < 32; u++)
|
||||
if ( (keys & (1 << u)) && (currentConfig.KeyBinds[u] & acts_c) ) {
|
||||
allActions[pl] |= acts_c & currentConfig.KeyBinds[u];
|
||||
keys &= ~((1 << i) | (1 << u));
|
||||
|
@ -650,25 +651,78 @@ static void simpleWait(int thissec, int lim_time)
|
|||
|
||||
spend_cycles(1024);
|
||||
gettimeofday(&tval, 0);
|
||||
if(thissec != tval.tv_sec) tval.tv_usec+=1000000;
|
||||
if (thissec != tval.tv_sec) tval.tv_usec+=1000000;
|
||||
|
||||
while(tval.tv_usec < lim_time)
|
||||
if (tval.tv_usec < lim_time)
|
||||
sched_yield();
|
||||
|
||||
while (tval.tv_usec < lim_time)
|
||||
{
|
||||
spend_cycles(1024);
|
||||
gettimeofday(&tval, 0);
|
||||
if(thissec != tval.tv_sec) tval.tv_usec+=1000000;
|
||||
if (thissec != tval.tv_sec) tval.tv_usec+=1000000;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
static void tga_dump(void)
|
||||
{
|
||||
#define BYTE unsigned char
|
||||
#define WORD unsigned short
|
||||
struct
|
||||
{
|
||||
BYTE IDLength; /* 00h Size of Image ID field */
|
||||
BYTE ColorMapType; /* 01h Color map type */
|
||||
BYTE ImageType; /* 02h Image type code */
|
||||
WORD CMapStart; /* 03h Color map origin */
|
||||
WORD CMapLength; /* 05h Color map length */
|
||||
BYTE CMapDepth; /* 07h Depth of color map entries */
|
||||
WORD XOffset; /* 08h X origin of image */
|
||||
WORD YOffset; /* 0Ah Y origin of image */
|
||||
WORD Width; /* 0Ch Width of image */
|
||||
WORD Height; /* 0Eh Height of image */
|
||||
BYTE PixelDepth; /* 10h Image pixel size */
|
||||
BYTE ImageDescriptor; /* 11h Image descriptor byte */
|
||||
} __attribute__((packed)) TGAHEAD;
|
||||
static unsigned short oldscr[320*240];
|
||||
FILE *f; char name[128]; int i;
|
||||
|
||||
memset(&TGAHEAD, 0, sizeof(TGAHEAD));
|
||||
TGAHEAD.ImageType = 2;
|
||||
TGAHEAD.Width = 320;
|
||||
TGAHEAD.Height = 240;
|
||||
TGAHEAD.PixelDepth = 16;
|
||||
TGAHEAD.ImageDescriptor = 2<<4; // image starts at top-left
|
||||
|
||||
#define CONV(X) (((X>>1)&0x7fe0)|(X&0x1f)) // 555?
|
||||
|
||||
for (i = 0; i < 320*240; i++)
|
||||
if(oldscr[i] != CONV(((unsigned short *)gp2x_screen)[i])) break;
|
||||
if (i < 320*240)
|
||||
{
|
||||
for (i = 0; i < 320*240; i++)
|
||||
oldscr[i] = CONV(((unsigned short *)gp2x_screen)[i]);
|
||||
sprintf(name, "%05i.tga", Pico.m.frame_count);
|
||||
f = fopen(name, "wb");
|
||||
if (!f) { printf("!f\n"); exit(1); }
|
||||
fwrite(&TGAHEAD, 1, sizeof(TGAHEAD), f);
|
||||
fwrite(oldscr, 1, 320*240*2, f);
|
||||
fclose(f);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
void emu_Loop(void)
|
||||
{
|
||||
static int gp2x_old_clock = 200;
|
||||
static int PsndRate_old = 0, PicoOpt_old = 0, EmuOpt_old = 0, pal_old = 0;
|
||||
char fpsbuff[24]; // fps count c string
|
||||
struct timeval tval; // timing
|
||||
int thissec = 0, frames_done = 0, frames_shown = 0, oldmodes = 0;
|
||||
int target_fps, target_frametime, lim_time, vsync_offset, i;
|
||||
int pframes_done, pframes_shown, pthissec; // "period" frames, used for sync
|
||||
int frames_done, frames_shown, thissec; // actual frames
|
||||
int oldmodes = 0, target_fps, target_frametime, lim_time, vsync_offset, i;
|
||||
char *notice = 0;
|
||||
|
||||
printf("entered emu_Loop()\n");
|
||||
|
@ -747,7 +801,10 @@ void emu_Loop(void)
|
|||
} else
|
||||
vsync_offset = 0;
|
||||
|
||||
// loop?
|
||||
frames_done = frames_shown = thissec =
|
||||
pframes_done = pframes_shown = pthissec = 0;
|
||||
|
||||
// loop
|
||||
while (engineState == PGS_Running)
|
||||
{
|
||||
int modes;
|
||||
|
@ -755,8 +812,8 @@ void emu_Loop(void)
|
|||
gettimeofday(&tval, 0);
|
||||
if (reset_timing) {
|
||||
reset_timing = 0;
|
||||
thissec = tval.tv_sec;
|
||||
frames_shown = frames_done = tval.tv_usec/target_frametime;
|
||||
pthissec = tval.tv_sec;
|
||||
pframes_shown = pframes_done = tval.tv_usec/target_frametime;
|
||||
}
|
||||
|
||||
// show notice message?
|
||||
|
@ -816,42 +873,50 @@ void emu_Loop(void)
|
|||
sprintf(fpsbuff, "%02i/%02i", frames_shown, frames_done);
|
||||
if (fpsbuff[5] == 0) { fpsbuff[5] = fpsbuff[6] = ' '; fpsbuff[7] = 0; }
|
||||
#endif
|
||||
frames_shown = frames_done = 0;
|
||||
thissec = tval.tv_sec;
|
||||
|
||||
if (PsndOut == 0 && currentConfig.Frameskip >= 0) {
|
||||
frames_done = frames_shown = 0;
|
||||
} else {
|
||||
// it is quite common for this implementation to leave 1 fame unfinished
|
||||
// when second changes, but we don't want buffer to starve.
|
||||
if(PsndOut && frames_done < target_fps && frames_done > target_fps-5) {
|
||||
updateKeys();
|
||||
SkipFrame(1); frames_done++;
|
||||
}
|
||||
|
||||
frames_done -= target_fps; if (frames_done < 0) frames_done = 0;
|
||||
frames_shown -= target_fps; if (frames_shown < 0) frames_shown = 0;
|
||||
if (frames_shown > frames_done) frames_shown = frames_done;
|
||||
}
|
||||
}
|
||||
#ifdef PFRAMES
|
||||
sprintf(fpsbuff, "%i", Pico.m.frame_count);
|
||||
#endif
|
||||
|
||||
lim_time = (frames_done+1) * target_frametime + vsync_offset;
|
||||
if(currentConfig.Frameskip >= 0) { // frameskip enabled
|
||||
if (pthissec != tval.tv_sec)
|
||||
{
|
||||
if (PsndOut == 0 && currentConfig.Frameskip >= 0) {
|
||||
pframes_done = pframes_shown = 0;
|
||||
} else {
|
||||
// it is quite common for this implementation to leave 1 fame unfinished
|
||||
// when second changes, but we don't want buffer to starve.
|
||||
if(PsndOut && pframes_done < target_fps && pframes_done > target_fps-5) {
|
||||
updateKeys();
|
||||
SkipFrame(1); pframes_done++;
|
||||
}
|
||||
|
||||
pframes_done -= target_fps; if (pframes_done < 0) pframes_done = 0;
|
||||
pframes_shown -= target_fps; if (pframes_shown < 0) pframes_shown = 0;
|
||||
if (pframes_shown > pframes_done) pframes_shown = pframes_done;
|
||||
}
|
||||
pthissec = tval.tv_sec;
|
||||
}
|
||||
|
||||
lim_time = (pframes_done+1) * target_frametime + vsync_offset;
|
||||
if (currentConfig.Frameskip >= 0) // frameskip enabled
|
||||
{
|
||||
for(i = 0; i < currentConfig.Frameskip; i++) {
|
||||
updateKeys();
|
||||
SkipFrame(1); frames_done++;
|
||||
SkipFrame(1); pframes_done++; frames_done++;
|
||||
if (PsndOut && !reset_timing) { // do framelimitting if sound is enabled
|
||||
gettimeofday(&tval, 0);
|
||||
if(thissec != tval.tv_sec) tval.tv_usec+=1000000;
|
||||
if(tval.tv_usec < lim_time) { // we are too fast
|
||||
simpleWait(thissec, lim_time);
|
||||
if (pthissec != tval.tv_sec) tval.tv_usec+=1000000;
|
||||
if (tval.tv_usec < lim_time) { // we are too fast
|
||||
simpleWait(pthissec, lim_time);
|
||||
}
|
||||
}
|
||||
lim_time += target_frametime;
|
||||
}
|
||||
} else if(tval.tv_usec > lim_time) { // auto frameskip
|
||||
}
|
||||
else if (tval.tv_usec > lim_time) // auto frameskip
|
||||
{
|
||||
// no time left for this frame - skip
|
||||
if (tval.tv_usec - lim_time >= 300000) {
|
||||
/* something caused a slowdown for us (disk access? cache flush?)
|
||||
|
@ -860,74 +925,16 @@ void emu_Loop(void)
|
|||
continue;
|
||||
}
|
||||
updateKeys();
|
||||
SkipFrame(tval.tv_usec < lim_time+target_frametime*2); frames_done++;
|
||||
SkipFrame(tval.tv_usec < lim_time+target_frametime*2); pframes_done++; frames_done++;
|
||||
continue;
|
||||
}
|
||||
|
||||
updateKeys();
|
||||
PicoFrame();
|
||||
|
||||
#if 0
|
||||
if (Pico.m.frame_count == 31563) {
|
||||
FILE *f;
|
||||
f = fopen("ram_p.bin", "wb");
|
||||
if (!f) { printf("!f\n"); exit(1); }
|
||||
fwrite(Pico.ram, 1, 0x10000, f);
|
||||
fclose(f);
|
||||
exit(0);
|
||||
}
|
||||
#endif
|
||||
#if 0
|
||||
// debug
|
||||
{
|
||||
#define BYTE unsigned char
|
||||
#define WORD unsigned short
|
||||
struct
|
||||
{
|
||||
BYTE IDLength; /* 00h Size of Image ID field */
|
||||
BYTE ColorMapType; /* 01h Color map type */
|
||||
BYTE ImageType; /* 02h Image type code */
|
||||
WORD CMapStart; /* 03h Color map origin */
|
||||
WORD CMapLength; /* 05h Color map length */
|
||||
BYTE CMapDepth; /* 07h Depth of color map entries */
|
||||
WORD XOffset; /* 08h X origin of image */
|
||||
WORD YOffset; /* 0Ah Y origin of image */
|
||||
WORD Width; /* 0Ch Width of image */
|
||||
WORD Height; /* 0Eh Height of image */
|
||||
BYTE PixelDepth; /* 10h Image pixel size */
|
||||
BYTE ImageDescriptor; /* 11h Image descriptor byte */
|
||||
} __attribute__((packed)) TGAHEAD;
|
||||
static unsigned short oldscr[320*240];
|
||||
FILE *f; char name[128]; int i;
|
||||
|
||||
memset(&TGAHEAD, 0, sizeof(TGAHEAD));
|
||||
TGAHEAD.ImageType = 2;
|
||||
TGAHEAD.Width = 320;
|
||||
TGAHEAD.Height = 240;
|
||||
TGAHEAD.PixelDepth = 16;
|
||||
TGAHEAD.ImageDescriptor = 2<<4; // image starts at top-left
|
||||
|
||||
#define CONV(X) (((X>>1)&0x7fe0)|(X&0x1f)) // 555?
|
||||
|
||||
for (i = 0; i < 320*240; i++)
|
||||
if(oldscr[i] != CONV(((unsigned short *)gp2x_screen)[i])) break;
|
||||
if (i < 320*240)
|
||||
{
|
||||
for (i = 0; i < 320*240; i++)
|
||||
oldscr[i] = CONV(((unsigned short *)gp2x_screen)[i]);
|
||||
sprintf(name, "%05i.tga", Pico.m.frame_count);
|
||||
f = fopen(name, "wb");
|
||||
if (!f) { printf("!f\n"); exit(1); }
|
||||
fwrite(&TGAHEAD, 1, sizeof(TGAHEAD), f);
|
||||
fwrite(oldscr, 1, 320*240*2, f);
|
||||
fclose(f);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// check time
|
||||
gettimeofday(&tval, 0);
|
||||
if (thissec != tval.tv_sec) tval.tv_usec+=1000000;
|
||||
if (pthissec != tval.tv_sec) tval.tv_usec+=1000000;
|
||||
|
||||
if (currentConfig.Frameskip < 0 && tval.tv_usec - lim_time >= 300000) // slowdown detection
|
||||
reset_timing = 1;
|
||||
|
@ -940,17 +947,18 @@ if (Pico.m.frame_count == 31563) {
|
|||
// we are too fast
|
||||
if (vsync_offset) {
|
||||
if (lim_time - tval.tv_usec > target_frametime/2)
|
||||
simpleWait(thissec, lim_time - target_frametime/4);
|
||||
simpleWait(pthissec, lim_time - target_frametime/4);
|
||||
gp2x_video_wait_vsync();
|
||||
} else {
|
||||
simpleWait(thissec, lim_time);
|
||||
simpleWait(pthissec, lim_time);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
blit(fpsbuff, notice);
|
||||
|
||||
frames_done++; frames_shown++;
|
||||
pframes_done++; pframes_shown++;
|
||||
frames_done++; frames_shown++;
|
||||
}
|
||||
|
||||
change_fast_forward(0);
|
||||
|
@ -976,4 +984,3 @@ void emu_ResetGame(void)
|
|||
reset_timing = 1;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -765,10 +765,10 @@ static void updateKeys(void)
|
|||
pl = (acts >> 16) & 1;
|
||||
if (kb_combo_keys & (1 << i))
|
||||
{
|
||||
int u, acts_c = acts & kb_combo_acts;
|
||||
int u = i+1, acts_c = acts & kb_combo_acts;
|
||||
// let's try to find the other one
|
||||
if (acts_c) {
|
||||
for (u = i + 1; u < 32; u++)
|
||||
for (; u < 32; u++)
|
||||
if ( (keys & (1 << u)) && (currentConfig.KeyBinds[u] & acts_c) ) {
|
||||
allActions[pl] |= acts_c & currentConfig.KeyBinds[u];
|
||||
keys &= ~((1 << i) | (1 << u));
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue