180 lines
7.4 KiB
C++
180 lines
7.4 KiB
C++
/*
|
|
* Copyright (C) 2021 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* https://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include "GameControllerMappingUtils.h"
|
|
|
|
#include "GameControllerManager.h"
|
|
|
|
namespace paddleboat {
|
|
MappingTableSearch::MappingTableSearch()
|
|
: mappingRoot(nullptr),
|
|
vendorId(0),
|
|
productId(0),
|
|
minApi(0),
|
|
maxApi(0),
|
|
tableIndex(0),
|
|
mapEntryCount(0),
|
|
tableEntryCount(0),
|
|
tableMaxEntryCount(GameControllerManager::getRemapTableSize()) {}
|
|
|
|
MappingTableSearch::MappingTableSearch(
|
|
Paddleboat_Controller_Mapping_Data *mapRoot, int32_t entryCount)
|
|
: mappingRoot(mapRoot),
|
|
vendorId(0),
|
|
productId(0),
|
|
minApi(0),
|
|
maxApi(0),
|
|
tableIndex(0),
|
|
mapEntryCount(0),
|
|
tableEntryCount(entryCount),
|
|
tableMaxEntryCount(GameControllerManager::getRemapTableSize()) {}
|
|
|
|
void MappingTableSearch::initSearchParameters(const int32_t newVendorId,
|
|
const int32_t newProductId,
|
|
const int32_t newMinApi,
|
|
const int32_t newMaxApi) {
|
|
vendorId = newVendorId;
|
|
productId = newProductId;
|
|
minApi = newMinApi;
|
|
maxApi = newMaxApi;
|
|
tableIndex = 0;
|
|
}
|
|
|
|
bool GameControllerMappingUtils::findMatchingMapEntry(
|
|
MappingTableSearch *searchEntry) {
|
|
int32_t currentIndex = 0;
|
|
|
|
// Starting out with a linear search. Updating the map table is something
|
|
// that should only ever be done once at startup, if it actually takes an
|
|
// appreciable time to execute when working with a big remap dictionary,
|
|
// this is low-hanging fruit to optimize.
|
|
const Paddleboat_Controller_Mapping_Data *mapRoot =
|
|
searchEntry->mappingRoot;
|
|
while (currentIndex < searchEntry->tableEntryCount) {
|
|
const Paddleboat_Controller_Mapping_Data &mapEntry =
|
|
mapRoot[currentIndex];
|
|
if (mapEntry.vendorId > searchEntry->vendorId) {
|
|
// Passed by the search vendorId value, so we don't already exist in
|
|
// the table, set the current index as the insert point and bail
|
|
searchEntry->tableIndex = currentIndex;
|
|
return false;
|
|
} else if (searchEntry->vendorId == mapEntry.vendorId) {
|
|
if (mapEntry.productId > searchEntry->productId) {
|
|
// Passed by the search productId value, so we don't already
|
|
// exist in the table, set the current index as the insert point
|
|
// and bail
|
|
searchEntry->tableIndex = currentIndex;
|
|
return false;
|
|
} else if (searchEntry->productId == mapEntry.productId) {
|
|
// Any overlap of the min/max API range is treated as matching
|
|
// an existing entry
|
|
if ((searchEntry->minApi >= mapEntry.minimumEffectiveApiLevel &&
|
|
searchEntry->minApi <=
|
|
mapEntry.maximumEffectiveApiLevel) ||
|
|
(searchEntry->minApi >= mapEntry.minimumEffectiveApiLevel &&
|
|
mapEntry.maximumEffectiveApiLevel == 0)) {
|
|
searchEntry->tableIndex = currentIndex;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
++currentIndex;
|
|
}
|
|
searchEntry->tableIndex = currentIndex;
|
|
return false;
|
|
}
|
|
|
|
bool GameControllerMappingUtils::insertMapEntry(
|
|
const Paddleboat_Controller_Mapping_Data *mappingData,
|
|
MappingTableSearch *searchEntry) {
|
|
bool insertSuccess = false;
|
|
// Verify there is room in the table for another entry
|
|
if (searchEntry->tableEntryCount < searchEntry->tableMaxEntryCount &&
|
|
searchEntry->tableIndex < searchEntry->tableMaxEntryCount) {
|
|
// Empty table, or inserting at the end, no relocation of existing data
|
|
// required, otherwise shift existing data down starting at the insert
|
|
// index.
|
|
if (!(searchEntry->tableEntryCount == 0 ||
|
|
searchEntry->tableIndex == searchEntry->tableEntryCount)) {
|
|
const size_t copySize =
|
|
(searchEntry->tableEntryCount - searchEntry->tableIndex) *
|
|
sizeof(Paddleboat_Controller_Mapping_Data);
|
|
memmove(&searchEntry->mappingRoot[searchEntry->tableIndex + 1],
|
|
&searchEntry->mappingRoot[searchEntry->tableIndex],
|
|
copySize);
|
|
}
|
|
// Insert the new data
|
|
memcpy(&searchEntry->mappingRoot[searchEntry->tableIndex], mappingData,
|
|
sizeof(Paddleboat_Controller_Mapping_Data));
|
|
insertSuccess = true;
|
|
}
|
|
return insertSuccess;
|
|
}
|
|
|
|
const Paddleboat_Controller_Mapping_Data *
|
|
GameControllerMappingUtils::validateMapTable(
|
|
const Paddleboat_Controller_Mapping_Data *mappingRoot,
|
|
const int32_t tableEntryCount) {
|
|
// The map table is always assumed to be sorted by increasing vendorId. Each
|
|
// sequence of entries with the same vendorId are sorted by increasing
|
|
// productId. Multiple entries with the same vendorId/productId range are
|
|
// sorted by increasing min/max API ranges. vendorId
|
|
// productId
|
|
// minApi
|
|
int32_t currentIndex = 0;
|
|
int32_t previousVendorId = -1;
|
|
while (currentIndex < tableEntryCount) {
|
|
if (mappingRoot[currentIndex].vendorId < previousVendorId) {
|
|
// failure in vendorId order, return the offending entry
|
|
return &mappingRoot[currentIndex];
|
|
}
|
|
|
|
int32_t previousProductId = mappingRoot[currentIndex].productId;
|
|
int32_t previousMinApi =
|
|
mappingRoot[currentIndex].minimumEffectiveApiLevel;
|
|
int32_t previousMaxApi =
|
|
mappingRoot[currentIndex].maximumEffectiveApiLevel;
|
|
previousVendorId = mappingRoot[currentIndex++].vendorId;
|
|
|
|
while (currentIndex < tableEntryCount &&
|
|
mappingRoot[currentIndex].vendorId == previousVendorId) {
|
|
while (currentIndex < tableEntryCount &&
|
|
mappingRoot[currentIndex].productId == previousProductId) {
|
|
if (mappingRoot[currentIndex].minimumEffectiveApiLevel <
|
|
previousMinApi ||
|
|
mappingRoot[currentIndex].minimumEffectiveApiLevel <
|
|
previousMaxApi) {
|
|
// failure in API order, return the offending entry
|
|
return &mappingRoot[currentIndex];
|
|
}
|
|
previousMinApi =
|
|
mappingRoot[currentIndex].minimumEffectiveApiLevel;
|
|
previousMaxApi =
|
|
mappingRoot[currentIndex++].maximumEffectiveApiLevel;
|
|
}
|
|
if (mappingRoot[currentIndex].productId < previousProductId) {
|
|
// failure in productId order, return the offending entry
|
|
return &mappingRoot[currentIndex];
|
|
}
|
|
previousProductId = mappingRoot[currentIndex++].productId;
|
|
}
|
|
}
|
|
|
|
// Validation success, return nullptr (no offending entries to return)
|
|
return nullptr;
|
|
}
|
|
} // namespace paddleboat
|