int test_outputfunc() { LOG *g = NULL ; g = CreateLogHandle() ; if( g == NULL ) { printf( "创建日志句柄失败errno[%d]\n" , errno ); return -1; } else { printf( "创建日志句柄成功\n" ); } SetLogOutput( g , LOG_OUTPUT_CALLBACK , "127.0.0.1:514" , & MyOpenLogFirst , NULL , & MyWriteLog , NULL , NULL , & MyCloseLogFinally ); SetLogLevel( g , LOG_LEVEL_INFO ); SetLogStyles( g , LOG_STYLES_LOG , LOG_NO_STYLEFUNC ); DebugLog( g , __FILE__ , __LINE__ , "hello iLOG3\n" ); InfoLog( g , __FILE__ , __LINE__ , "hello iLOG3\n" ); WarnLog( g , __FILE__ , __LINE__ , "hello iLOG3\n" ); ErrorLog( g , __FILE__ , __LINE__ , "hello iLOG3\n" ); FatalLog( g , __FILE__ , __LINE__ , "hello iLOG3\n" ); DestroyLogHandle( g ); printf( "销毁日志句柄\n" ); return 0; }
void Map::Init4bpp(const Image16Bpp& image) { for (unsigned int i = 0; i < data.size(); i++) { int tilex = i % width; int tiley = i / width; ImageTile imageTile(image, tilex, tiley); int tile_id = 0; int pal_id = 0; if (!tileset->Match(imageTile, tile_id, pal_id)) { WarnLog("Image: %s No match for tile starting at (%d %d) px, using empty tile instead.", image.name.c_str(), tilex * 8, tiley * 8); WarnLog("Image: %s No match for palette for tile starting at (%d %d) px, using palette 0 instead.", image.name.c_str(), tilex * 8, tiley * 8); } VerboseLog("%d %d => %d %d", tilex, tiley, pal_id, tile_id); data[i] = pal_id << 12 | tile_id; } }
int CmdLineParserHelper::GetHexInt(const std::string& param, int def_value, int min, int max) { wxString hex; if (!parser.Found(param, &hex)) return def_value; long ret; if (!hex.ToLong(&ret, 16)) { WarnLog("Could not parse %s into hex ignoring", param.c_str()); return def_value; } int val = ret; if (val < min || val > max) { WarnLog("Invalid value given for %s range [%d %d] given %d ignoring", param.c_str(), min, max, val); val = def_value; } return val; }
int CmdLineParserHelper::GetInt(const std::string& param, int def_value, int min, int max) { long ret; if (!parser.Found(param, &ret)) return def_value; int val = ret; if (val < min || val > max) { WarnLog("Invalid value given for %s range [%d %d] given %d ignoring", param.c_str(), min, max, val); val = def_value; } return val; }
void Map::Init8bpp(const Image16Bpp& image) { for (unsigned int i = 0; i < data.size(); i++) { int tilex = i % width; int tiley = i / width; ImageTile tile(image, tilex, tiley); int tile_id = 0; int pal_id = 0; if (!tileset->Match(tile, tile_id, pal_id)) WarnLog("Image: %s No match for tile starting at (%d %d) px, using empty tile instead.", image.name.c_str(), tilex * 8, tiley * 8); data[i] = tile_id; } }
double RawData::getBlockAveEnergy(int index){ double ret = 0; int st = index *SAMPLES_IN_EACH_FRAME; // overflow if(st > frame_num){ WarnLog("Calculate average energy in %d block, max %d", index,frame_num/SAMPLES_IN_EACH_FRAME); return ret; } //calculating for(int i = 0;i<SAMPLES_IN_EACH_FRAME;i++){ double x = data[st+i]; ret += x*x; } ret /= SAMPLES_IN_EACH_FRAME; return ret; }
void Nin10Edit::OnOpen(wxCommandEvent& event) { EventLog l(__func__); std::unique_ptr<wxFileDialog> filedlg(new wxFileDialog(this, _("Open Image"), "", "", wxFileSelectorDefaultWildcardStr, wxFD_FILE_MUST_EXIST | wxFD_CHANGE_DIR | wxFD_PREVIEW | wxFD_OPEN)); if (filedlg->ShowModal() == wxID_CANCEL) { WarnLog("No image given"); return; } std::string file = filedlg->GetPath().ToStdString(); InfoLog("Got file: %s", file.c_str()); images.emplace(file, file); ConvertToMode4(images, images8); auto* graphics_panel = new GraphicsEditorPanel(graphics_notebook); graphics_notebook->AddPage(graphics_panel, file, false); graphics_panel->SetImage(&images8[0]); palette_panel->SetPalette(images8[0].palette); }
BOOL CheckForGarbage ( Slot_Mgr_Shr_t *MemPtr ) { int SlotIndex; int ProcIndex; int Err; BOOL ValidPid; ASSERT( MemPtr != NULL_PTR ); #ifdef DEV DbgLog(DL5, "Thread %d is checking for garbage", pthread_self()); #endif /* DEV */ #ifdef DEV DbgLog (DL5, "Garbage collection attempting global shared memory lock"); #endif /* DEV */ /* Grab the global Shared mem mutex since we might modify global_session_count */ #ifdef PKCS64 Err = msem_lock(&(MemPtr->slt_mutex),0); #else Err = pthread_mutex_lock ( &(MemPtr->slt_mutex) ); #endif if ( Err != 0 ) { DbgLog (DL0, "Garbage collection: Locking attempt for global shmem mutex returned %s", SysConst(Err) ); return FALSE; } #ifdef DEV DbgLog ( DL5, "Garbage collection: Got global shared memory lock"); #endif /* DEV */ for ( ProcIndex = 0; ProcIndex < NUMBER_PROCESSES_ALLOWED; ProcIndex++ ) { #ifdef PKCS64 Slot_Mgr_Proc_t_64 *pProc = &(MemPtr->proc_table[ProcIndex]); #else Slot_Mgr_Proc_t *pProc = &(MemPtr->proc_table[ProcIndex]); #endif ASSERT(pProc != NULL_PTR); if ( ! (pProc->inuse) ) { continue; } ValidPid = ( (IsValidProcessEntry ( pProc->proc_id, pProc->reg_time )) && (pProc->proc_id != 0) ); if ( ( pProc->inuse ) && (! ValidPid ) ) { #ifdef DEV DbgLog(DL1, "Garbage collection routine found bad entry for pid %d (Index: %d); removing from table", pProc->proc_id, ProcIndex ); #endif /* DEV */ /* */ /* Clean up session counts */ /* */ for ( SlotIndex = 0; SlotIndex < NUMBER_SLOTS_MANAGED; SlotIndex++ ) { #ifdef PKCS64 unsigned int *pGlobalSessions = &(MemPtr->slot_info[SlotIndex].global_sessions); unsigned int *pProcSessions = &(pProc->slot_session_count[SlotIndex]); #else int *pGlobalSessions = &(MemPtr->slot_info[SlotIndex].global_sessions); int *pProcSessions = &(pProc->slot_session_count[SlotIndex]); #endif if ( *pProcSessions > 0 ) { #ifdef DEV DbgLog ( DL2, "GC: Invalid pid (%d) is holding %d sessions open on slot %d. Global session count for this slot is %d", pProc->proc_id, *pProcSessions, SlotIndex, *pGlobalSessions ); #endif /* DEV */ if ( *pProcSessions > *pGlobalSessions ) { #ifdef DEV WarnLog ( "Garbage Collection: Illegal values in table for defunct process"); DbgLog ( DL0, "Garbage collection: A process ( Index: %d, pid: %d ) showed %d sessions open on slot %s, but the global count for this slot is only %d", ProcIndex, pProc->proc_id, *pProcSessions, SlotIndex, *pGlobalSessions ); #endif /* DEV */ *pGlobalSessions = 0; } else { *pGlobalSessions -= *pProcSessions; } *pProcSessions = 0; } /* end if *pProcSessions */ } /* end for SlotIndex */ /* */ /* NULL out everything except the mutex */ /* */ #if PER_PROCESS_MUTEXES /* Grab the mutex for this proc's shared memory data structure */ #ifdef PKCS64 Err = msem_lock(&(pProc->proc_mutex),MSEM_IF_NOWAIT); #else Err = pthread_mutex_trylock( &(pProc->proc_mutex) ); #endif if ( ( Err != 0 ) ) { /* We didn't get the lock! */ /* Attempting to destroy a locked mutex results in undefined behavior */ /* http://techlib.austin.ibm.com/techlib/manuals/adoclib/libs/basetrf1/pthreads.htm */ DbgLog (DL0,"Unable to get per-process mutex for pid %d (%s) - skipping", pProc->proc_id, SysConst( Err ) ); /* The exit routine will figure out that this is an invalid process entry (by calling IsValidProcessEntry()), and won't prevent the slotd from exiting because of this entry. */ continue; } #endif /* PER_PROCESS_MUTEXES */ memset( &(pProc->inuse), '\0', sizeof(pProc->inuse) ); memset( &(pProc->proc_id), '\0', sizeof(pProc->proc_id) ); memset( &(pProc->slotmap), '\0', sizeof(pProc->slotmap) ); memset( &(pProc->blocking), '\0', sizeof(pProc->blocking )); memset( &(pProc->error), '\0', sizeof(pProc->error) ); memset( &(pProc->slot_session_count), '\0', sizeof(pProc->slot_session_count) ); memset( &(pProc->reg_time), '\0', sizeof(pProc->reg_time) ); } /* end if inuse && ValidPid */ #if PER_PROCESS_MUTEXES #ifdef PKCS64 msem_unlock(&(pProc->proc_mutex),0) #else pthread_mutex_unlock( &(pProc->proc_mutex)); #endif #endif /* PER_PROCESS_MUTEXES */ } /* end for ProcIndex */ #ifdef PKCS64 msem_unlock(&(MemPtr->slt_mutex),0); #else pthread_mutex_unlock ( &(MemPtr->slt_mutex) ); #endif DbgLog ( DL5, "Garbage collection: Released global shared memory lock"); return TRUE; }
BOOL CheckForGarbage ( Slot_Mgr_Shr_t *MemPtr ) { int SlotIndex; int ProcIndex; int Err; BOOL ValidPid; ASSERT( MemPtr != NULL_PTR ); #ifdef DEV DbgLog(DL5, "Thread %d is checking for garbage", pthread_self()); #endif /* DEV */ #ifdef DEV DbgLog (DL5, "Garbage collection attempting global shared memory lock"); #endif /* DEV */ /* Grab the global Shared mem mutex since we might modify global_session_count */ Err = XProcLock(); if ( Err != TRUE ) { DbgLog (DL0, "Garbage collection: Locking attempt for global shmem mutex returned %s", SysConst(Err) ); return FALSE; } #ifdef DEV DbgLog ( DL5, "Garbage collection: Got global shared memory lock"); #endif /* DEV */ for ( ProcIndex = 0; ProcIndex < NUMBER_PROCESSES_ALLOWED; ProcIndex++ ) { Slot_Mgr_Proc_t_64 *pProc = &(MemPtr->proc_table[ProcIndex]); ASSERT(pProc != NULL_PTR); if ( ! (pProc->inuse) ) { continue; } ValidPid = ( (IsValidProcessEntry ( pProc->proc_id, pProc->reg_time )) && (pProc->proc_id != 0) ); if ( ( pProc->inuse ) && (! ValidPid ) ) { #ifdef DEV DbgLog(DL1, "Garbage collection routine found bad entry for pid %d (Index: %d); removing from table", pProc->proc_id, ProcIndex ); #endif /* DEV */ /* */ /* Clean up session counts */ /* */ for ( SlotIndex = 0; SlotIndex < NUMBER_SLOTS_MANAGED; SlotIndex++ ) { unsigned int *pGlobalSessions = &(MemPtr->slot_global_sessions[SlotIndex]); unsigned int *pProcSessions = &(pProc->slot_session_count[SlotIndex]); if ( *pProcSessions > 0 ) { #ifdef DEV DbgLog ( DL2, "GC: Invalid pid (%d) is holding %d sessions open on slot %d. Global session count for this slot is %d", pProc->proc_id, *pProcSessions, SlotIndex, *pGlobalSessions ); #endif /* DEV */ if ( *pProcSessions > *pGlobalSessions ) { #ifdef DEV WarnLog ( "Garbage Collection: Illegal values in table for defunct process"); DbgLog ( DL0, "Garbage collection: A process ( Index: %d, pid: %d ) showed %d sessions open on slot %s, but the global count for this slot is only %d", ProcIndex, pProc->proc_id, *pProcSessions, SlotIndex, *pGlobalSessions ); #endif /* DEV */ *pGlobalSessions = 0; } else { *pGlobalSessions -= *pProcSessions; } *pProcSessions = 0; } /* end if *pProcSessions */ } /* end for SlotIndex */ /* */ /* NULL out everything except the mutex */ /* */ memset( &(pProc->inuse), '\0', sizeof(pProc->inuse) ); memset( &(pProc->proc_id), '\0', sizeof(pProc->proc_id) ); memset( &(pProc->slotmap), '\0', sizeof(pProc->slotmap) ); memset( &(pProc->blocking), '\0', sizeof(pProc->blocking )); memset( &(pProc->error), '\0', sizeof(pProc->error) ); memset( &(pProc->slot_session_count), '\0', sizeof(pProc->slot_session_count) ); memset( &(pProc->reg_time), '\0', sizeof(pProc->reg_time) ); } /* end if inuse && ValidPid */ } /* end for ProcIndex */ XProcUnLock(); DbgLog ( DL5, "Garbage collection: Released global shared memory lock"); return TRUE; }
int worker( struct TcpdaemonEntryParam *pep , struct TcpdaemonServerEnv *pse ) { struct sembuf sb ; fd_set readfds ; struct sockaddr accept_addr ; socklen_t accept_addrlen ; int accept_sock ; int nret = 0 ; while(1) { DebugLog( __FILE__ , __LINE__ , "WORKER(%ld) | waiting for entering accept mutex\n" , pse->index ); /* 进入临界区 */ memset( & sb , 0x00 , sizeof(struct sembuf) ); sb.sem_num = 0 ; sb.sem_op = -1 ; sb.sem_flg = SEM_UNDO ; nret = semop( pse->accept_mutex , & sb , 1 ) ; if( nret == -1 ) { ErrorLog( __FILE__ , __LINE__ , "WORKER(%ld) | enter accept mutex failed , errno[%d]\n" , pse->index , errno ); return -1; } else { DebugLog( __FILE__ , __LINE__ , "WORKER(%ld) | enter accept mutex ok\n" , pse->index ); } /* 监控侦听socket或存活管道事件 */ FD_ZERO( & readfds ); FD_SET( pse->listen_sock , & readfds ); FD_SET( pse->alive_pipe->fd[0] , & readfds ); nret = select( MAX_INT(pse->listen_sock,pse->alive_pipe->fd[0])+1 , & readfds , NULL , NULL , NULL ) ; if( nret == -1 ) { ErrorLog( __FILE__ , __LINE__ , "WORKER(%ld) | select failed , errno[%d]\n" , pse->index , errno ); break; } if( FD_ISSET( pse->alive_pipe->fd[0] , & readfds ) ) { DebugLog( __FILE__ , __LINE__ , "WORKER(%ld) | alive_pipe received quit command\n" , pse->index ); break; } /* 接受新客户端连接 */ accept_addrlen = sizeof(struct sockaddr) ; memset( & accept_addr , 0x00 , accept_addrlen ); accept_sock = accept( pse->listen_sock , & accept_addr , & accept_addrlen ) ; if( accept_sock == -1 ) { ErrorLog( __FILE__ , __LINE__ , "WORKER(%ld) | accept failed , errno[%d]\n" , pse->index , errno ); break; } else { DebugLog( __FILE__ , __LINE__ , "WORKER(%ld) | accept ok , [%d]accept[%d]\n" , pse->index , pse->listen_sock , accept_sock ); } if( pep->tcp_nodelay > 0 ) { setsockopt( accept_sock , IPPROTO_TCP , TCP_NODELAY , (void*) & (pep->tcp_nodelay) , sizeof(int) ); } if( pep->tcp_linger > 0 ) { struct linger lg ; lg.l_onoff = 1 ; lg.l_linger = pep->tcp_linger - 1 ; setsockopt( accept_sock , SOL_SOCKET , SO_LINGER , (void *) & lg , sizeof(struct linger) ); } /* 离开临界区 */ memset( & sb , 0x00 , sizeof(struct sembuf) ); sb.sem_num = 0 ; sb.sem_op = 1 ; sb.sem_flg = SEM_UNDO ; nret = semop( pse->accept_mutex , & sb , 1 ) ; if( nret == -1 ) { ErrorLog( __FILE__ , __LINE__ , "WORKER(%ld) | leave accept mutex failed , errno[%d]\n" , pse->index , errno ); return -1; } else { DebugLog( __FILE__ , __LINE__ , "WORKER(%ld) | leave accept mutex ok\n" , pse->index ); } /* 调用通讯数据协议及应用处理回调函数 */ DebugLog( __FILE__ , __LINE__ , "WORKER(%ld) | 调用tcpmain\n" , pse->index ); nret = pse->pfunc_tcpmain( pep->param_tcpmain , accept_sock , & accept_addr ) ; if( nret < 0 ) { ErrorLog( __FILE__ , __LINE__ , "WORKER(%ld) | tcpmain return[%d]\n" , pse->index , nret ); return -1; } else if( nret > 0 ) { WarnLog( __FILE__ , __LINE__ , "WORKER(%ld) | tcpmain return[%d]\n" , pse->index , nret ); } else { DebugLog( __FILE__ , __LINE__ , "WORKER(%ld) | tcpmain return[%d]\n" , pse->index , nret ); } /* 关闭客户端连接 */ close( accept_sock ); DebugLog( __FILE__ , __LINE__ , "close[%d]\n" , accept_sock ); /* 检查工作进程处理数量 */ pse->requests_per_process++; if( pep->max_requests_per_process != 0 && pse->requests_per_process >= pep->max_requests_per_process ) { InfoLog( __FILE__ , __LINE__ , "WORKER(%ld) | maximum number of processing[%ld][%ld] , ending\n" , pse->index , pse->requests_per_process , pep->max_requests_per_process ); return -1; } } /* 最终离开临界区 */ memset( & sb , 0x00 , sizeof(struct sembuf) ); sb.sem_num = 0 ; sb.sem_op = 1 ; sb.sem_flg = SEM_UNDO ; nret = semop( pse->accept_mutex , & sb , 1 ) ; if( nret == -1 ) { InfoLog( __FILE__ , __LINE__ , "WORKER(%ld) | leave accept mutex finally failed , errno[%d]\n" , pse->index , errno ); return -1; } else { DebugLog( __FILE__ , __LINE__ , "WORKER(%ld) | leave accept mutex finally ok\n" , pse->index ); } return 0; }
void InitMediaMananger() { InitializeCriticalSection(&g_locker); HKEY hKey; LONG lRet; lRet = RegOpenKeyEx(HKEY_CURRENT_USER, REG_PATH, 0, KEY_QUERY_VALUE | KEY_SET_VALUE, &hKey ); if (lRet == ERROR_FILE_NOT_FOUND) { return ; } if( lRet != ERROR_SUCCESS ) { WarnLog("Faild(%d) to RegOpenKeyEx\n", lRet); return ; } for(int i = 0; ; i++) { wchar_t achGuid[256]; DWORD cchGuid = 256; struct MediaInfo info; if (RegEnumValue(hKey, i, achGuid, &cchGuid, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) break; DWORD cbData = REG_SIZE; DWORD dwType = REG_BINARY; lRet = RegQueryValueEx(hKey, achGuid, 0, &dwType, (LPBYTE)(&info), &cbData); if ((lRet != ERROR_SUCCESS ) || (dwType != REG_BINARY) || (cbData != REG_SIZE)) { ErrorLog("Faild(%d) to RegQueryValueEx\n", lRet); RegDeleteValue(hKey, achGuid); continue; } if (CheckMediaInfo(&info) == FALSE) { lRet = RegDeleteValue(hKey, achGuid); continue; } g_media_list = (MediaInfo **)MemoryRealloc(g_media_list, (g_media_info_count + 1) * sizeof(MediaInfo*)); g_media_list[g_media_info_count] = (MediaInfo *)MemoryAlloc(sizeof(MediaInfo)); g_media_list[g_media_info_count][0] = info; g_media_info_count ++; } RegCloseKey( hKey ); if (g_media_info_count > 1) { qsort(g_media_list, g_media_info_count, sizeof(MediaInfo *), (int32_t (__cdecl *)(const void *,const void *))mediainfo_compare); } // ɾ³ý wchar_t szFileFormat[MAX_PATH]; StringCchPrintf(szFileFormat, MAX_PATH, L"%ls\\vmeisoft_v1_*.*", g_szTempPath); WIN32_FIND_DATAW wfd; HANDLE hFind = FindFirstFile(szFileFormat, &wfd); if (hFind != INVALID_HANDLE_VALUE) { do { GUID id = GetGuidFromFileName(wfd.cFileName); BOOL bFound = FALSE; for(int i = 0; i < g_media_info_count; i++) { if (g_media_list[i]->m_Id == id) { bFound = TRUE; break; } } if (bFound == FALSE) { wchar_t szFileName[MAX_PATH]; StringCchPrintf(szFileName, MAX_PATH, L"%ls\\%ls", g_szTempPath, wfd.cFileName); DeleteFile(szFileName); } } while (FindNextFile(hFind, &wfd) != 0); FindClose(hFind); } }
void Tileset::Init8bpp(const std::vector<Image16Bpp>& images16) { int tile_width = 8 + params.border; // Reduce all and get the global palette and reduced images. Image8BppScene scene(images16, name, palette); palette = scene.palette; const Tile& nullTile = Tile::GetNullTile8(); const ImageTile& nullImageTile = ImageTile::GetNullTile(); tiles.insert(nullTile); tilesExport.push_back(nullTile); matcher.insert(std::pair<ImageTile, Tile>(nullImageTile, nullTile)); for (unsigned int k = 0; k < images16.size(); k++) { const Image8Bpp& image = scene.GetImage(k); const Image16Bpp& image16 = images16[k]; unsigned int tilesX = image.width / tile_width; unsigned int tilesY = image.height / tile_width; unsigned int totalTiles = tilesX * tilesY; // Perform reduce. for (unsigned int i = 0; i < totalTiles; i++) { int tilex = i % tilesX; int tiley = i / tilesX; Tile tile(image, tilex, tiley, params.border); std::set<Tile>::iterator foundTile = tiles.find(tile); if (foundTile == tiles.end()) { tile.id = tiles.size(); tiles.insert(tile); tilesExport.push_back(tile); // Add matcher data ImageTile imageTile(image16, tilex, tiley, params.border); matcher.insert(std::pair<ImageTile, Tile>(imageTile, tile)); } } } // Checks int tile_size = TILE_SIZE_BYTES_8BPP; int memory_b = tiles.size() * tile_size; if (params.force) { if (!affine && tiles.size() >= 1024) WarnLog("Too many tiles. Found %d tiles. Maximum is 1024. Associated maps exported against this tileset may be incorrect.", tiles.size()); else if (affine && tiles.size() >= 256) WarnLog("Too many tiles found for affine. Found %d tiles. Maximum is 256. Associated maps exported against this tileset may be incorrect.", tiles.size()); } else { if (!affine && tiles.size() >= 1024) FatalLog("Too many tiles. Found %d tiles. Maximum is 1024. Please make the map/tileset simpler. Use --force to override this.", tiles.size()); else if (affine && tiles.size() >= 256) FatalLog("Too many tiles found for affine. Found %d tiles. Maximum is 256. Please make the map/tileset simpler. Use --force to override this.", tiles.size()); } // Delicious infos int cbbs = tiles.size() * tile_size / SIZE_CBB_BYTES; int sbbs = (int) ceil(tiles.size() * tile_size % SIZE_CBB_BYTES / ((double)SIZE_SBB_BYTES)); InfoLog("Tiles found %zu.", tiles.size()); InfoLog("Tiles uses %d charblocks and %d screenblocks.", cbbs, sbbs); InfoLog("Total utilization %.2f/4 charblocks or %d/32 screenblocks, %d/65536 bytes.", memory_b / ((double)SIZE_CBB_BYTES), (int) ceil(memory_b / ((double)SIZE_SBB_BYTES)), memory_b); }
void Tileset::Init4bpp(const std::vector<Image16Bpp>& images) { // Tile image into 16 bit tiles Tileset tileset16bpp(images, name, 16, affine); std::set<ImageTile> imageTiles = tileset16bpp.itiles; const Tile& nullTile = Tile::GetNullTile4(); const ImageTile& nullImageTile = ImageTile::GetNullTile(); tiles.insert(nullTile); tilesExport.push_back(nullTile); matcher.insert(std::pair<ImageTile, Tile>(nullImageTile, nullTile)); // Reduce each tile to 4bpp std::vector<Tile> gbaTiles; for (const auto& imageTile : imageTiles) gbaTiles.emplace_back(imageTile, 4); // Ensure image contains < 256 colors std::set<Color16> bigPalette; for (const auto& tile : gbaTiles) { const std::vector<Color16>& tile_palette = tile.palette.GetColors(); bigPalette.insert(tile_palette.begin(), tile_palette.end()); } if (bigPalette.size() > 256 && !params.force) FatalLog("Image after reducing tiles to 4 bpp still contains more than 256 distinct colors. Found %d colors. Please fix. Use --force to override.", bigPalette.size()); else if (bigPalette.size() > 256 && params.force) WarnLog("Image after reducing tiles to 4 bpp still contains more than 256 distinct colors. Found %d colors. Potential for image color quality loss."); // Greedy approach deal with tiles with largest palettes first. std::sort(gbaTiles.begin(), gbaTiles.end(), TilesPaletteSizeComp); // Construct palette banks, assign bank id to tile, remap tile to palette bank given, assign tile ids for (auto& tile : gbaTiles) { int pbank = -1; // Fully contains checks for (unsigned int i = 0; i < paletteBanks.Size(); i++) { PaletteBank& bank = paletteBanks[i]; if (bank.Contains(tile.palette)) pbank = i; } // Ok then find least affected bank if (pbank == -1) { int min_delta = 0x7FFFFFFF; for (unsigned int i = 0; i < paletteBanks.Size(); i++) { PaletteBank& bank = paletteBanks[i]; int colors_left; int delta; bank.CanMerge(tile.palette, colors_left, delta); if (colors_left >= 0 && delta < min_delta) { min_delta = delta; pbank = i; } } } // Cry and die for now. Unless you tell me to keep going. if (pbank == -1 && !params.force) FatalLog("More than 16 distinct palettes found, please use 8bpp mode. Use --force to override."); else if (pbank == -1 && params.force) WarnLog("More than 16 distinct palettes found. Huge potential for image color quality loss."); // Alright... if (pbank == -1) { pbank = paletteBanks.FindBestMatch(tile.palette); paletteBanks[pbank].BestMerge(tile.palette); } else { // Merge step and assign palette bank paletteBanks[pbank].Merge(tile.palette); } tile.palette_bank = pbank; tile.UsePalette(paletteBanks[pbank]); // Assign tile id const auto& it = tiles.find(tile); if (it == tiles.end()) { tile.id = tiles.size(); tiles.insert(tile); tilesExport.push_back(tile); } else { tile.id = it->id; } // Form mapping from ImageTile to Tile matcher.insert(std::pair<ImageTile, Tile>(*tile.sourceTile, tile)); } int tile_size = TILE_SIZE_BYTES_4BPP; int memory_b = tiles.size() * tile_size; // 4bpp mode so !affine can be assumed here. Affine maps have a max of 256 tiles. if (tiles.size() >= 1024 && !params.force) FatalLog("Too many tiles. Found %d tiles. Maximum is 1024. Please make the image simpler. Use --force to override.", tiles.size()); else if (tiles.size() >= 1024 && params.force) WarnLog("Too many tiles. Found %d tiles. Maximum is 1024. Associated maps exported against this tileset may be incorrect.", tiles.size()); // Delicious infos int cbbs = tiles.size() * tile_size / SIZE_CBB_BYTES; int sbbs = (int) ceil(tiles.size() * tile_size % SIZE_CBB_BYTES / ((double)SIZE_SBB_BYTES)); InfoLog("Tiles found %zu.", tiles.size()); InfoLog("Tiles uses %d charblocks and %d screenblocks.", cbbs, sbbs); InfoLog("Total utilization %.2f/4 charblocks or %d/32 screenblocks, %d/65536 bytes.", memory_b / ((double)SIZE_CBB_BYTES), (int) ceil(memory_b / ((double)SIZE_SBB_BYTES)), memory_b); }