@@ -279,22 +279,79 @@ private static class ModsInstallApi extends CommonApi {
279279 /**
280280 * Override getModDetails to filter versions by the selected MC version.
281281 * Only versions matching the filter are shown in the version dropdown.
282+ * Also resolves which (if any) of the returned versions is already
283+ * installed in the current instance's mods folder, so the install
284+ * button can show Install / Installed / Update / Downgrade.
282285 */
283286 @ Override
284287 public ModDetail getModDetails (ModItem item ) {
288+ ModDetail detail ;
285289 if (item .apiSource == net .kdt .pojavlaunch .modloaders .modpacks .models .Constants .SOURCE_MODRINTH ) {
286290 String filterVer = (mFilters .mcVersion != null && !mFilters .mcVersion .isEmpty ())
287291 ? mFilters .mcVersion : null ;
288292 String filterLoader = (mFilters .modLoader != null && !mFilters .modLoader .isEmpty ())
289293 ? mFilters .modLoader : null ;
290- return mModrinthApi .getModDetails (item , filterVer , filterLoader );
291- }
292- if (item .apiSource == net .kdt .pojavlaunch .modloaders .modpacks .models .Constants .SOURCE_CURSEFORGE ) {
294+ detail = mModrinthApi .getModDetails (item , filterVer , filterLoader );
295+ } else if (item .apiSource == net .kdt .pojavlaunch .modloaders .modpacks .models .Constants .SOURCE_CURSEFORGE ) {
293296 String filterVer = (mFilters .mcVersion != null && !mFilters .mcVersion .isEmpty ())
294297 ? mFilters .mcVersion : null ;
295- return mCurseforgeApi .getModDetails (item , filterVer );
298+ detail = mCurseforgeApi .getModDetails (item , filterVer );
299+ } else {
300+ detail = super .getModDetails (item );
301+ }
302+
303+ if (detail != null && !detail .isModpack ) {
304+ resolveInstalledVersionIndex (detail );
305+ }
306+ return detail ;
307+ }
308+
309+ /**
310+ * Hashes every jar currently in the mods folder (enabled or disabled)
311+ * and matches against detail.versionHashes to find which version, if
312+ * any, is already installed. Sets detail.installedVersionIndex
313+ * accordingly (-1 if this mod isn't installed at all).
314+ */
315+ private void resolveInstalledVersionIndex (ModDetail detail ) {
316+ detail .installedVersionIndex = -1 ;
317+ if (detail .versionHashes == null || detail .versionHashes .length == 0 ) return ;
318+
319+ File modsDir = getModsDir ();
320+ File [] files = modsDir .listFiles (f -> f .isFile () &&
321+ (f .getName ().endsWith (".jar" ) || f .getName ().endsWith (".jar.disabled" )));
322+ if (files == null || files .length == 0 ) return ;
323+
324+ java .util .Set <String > installedHashes = new java .util .HashSet <>();
325+ for (File f : files ) {
326+ String hash = sha1Hex (f );
327+ if (hash != null ) installedHashes .add (hash );
328+ }
329+ if (installedHashes .isEmpty ()) return ;
330+
331+ for (int i = 0 ; i < detail .versionHashes .length ; i ++) {
332+ String hash = detail .versionHashes [i ];
333+ if (hash != null && installedHashes .contains (hash .toLowerCase (java .util .Locale .ROOT ))) {
334+ detail .installedVersionIndex = i ;
335+ break ;
336+ }
337+ }
338+ }
339+
340+ private static String sha1Hex (File file ) {
341+ try {
342+ java .security .MessageDigest digest = java .security .MessageDigest .getInstance ("SHA-1" );
343+ byte [] buffer = new byte [8192 ];
344+ try (java .io .FileInputStream fis = new java .io .FileInputStream (file )) {
345+ int read ;
346+ while ((read = fis .read (buffer )) != -1 ) digest .update (buffer , 0 , read );
347+ }
348+ byte [] bytes = digest .digest ();
349+ StringBuilder sb = new StringBuilder (bytes .length * 2 );
350+ for (byte b : bytes ) sb .append (String .format ("%02x" , b ));
351+ return sb .toString ();
352+ } catch (Exception e ) {
353+ return null ;
296354 }
297- return super .getModDetails (item );
298355 }
299356
300357 @ Override
0 commit comments