Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lost class files when exporting jar #916

Open
luiox opened this issue Feb 15, 2025 · 3 comments
Open

Lost class files when exporting jar #916

luiox opened this issue Feb 15, 2025 · 3 comments

Comments

@luiox
Copy link

luiox commented Feb 15, 2025

Describe the bug

I am using the latest version of Recaf4 today. When I tried to export a jar without any modifications, I found that I originally had a 100MB jar, but after exporting it, I only had about 1MB left.

Because these classes are confused and named in the format xxx\u0000xxx.class, the characters before \u0000 are the same, and the file is only distinguished by the differences in the characters after \u0000. In this case, exporting with recaf would result in losing most of the files starting with xxx\u0000.

In the output window of recaf, it shows that the export has been successful, and the fact is that it has indeed been exported, but some class files have been discarded.

@luiox
Copy link
Author

luiox commented Feb 15, 2025

I'm not quite sure why this is happening, but through the debugging of Idea, I roughly understand the String originalName = PathOriginalNameProperty.get(classInfo); in the file 'recaf-ui\src\main\java\software\coley\recaf\workspace\PathExportingManager.java'; This line will discard the characters after \u0000, resulting in the same key. In fact, when the file name is used as the key, the person and file will not be lost.

Before:

	resource.jvmClassBundleStream().forEach(bundle -> {
		for (JvmClassInfo classInfo : bundle) {
			String key;
			String originalName = PathOriginalNameProperty.get(classInfo);
			if (originalName == null) {
				String pathPrefix = PathPrefixProperty.get(classInfo);
				String pathSuffix = Objects.requireNonNullElse(PathSuffixProperty.get(classInfo), ".class");
				key = classInfo.getName() + pathSuffix;
				if (pathPrefix != null)
					key = pathPrefix + key;
			} else {
				key = originalName;
			}
			map.put(key, classInfo.getBytecode());
			updateProperties(key, classInfo);
		}
	});

My Edition:

	resource.jvmClassBundleStream().forEach(bundle -> {
		for (JvmClassInfo classInfo : bundle) {
			String key;
			String originalName = PathOriginalNameProperty.get(classInfo);
			if (originalName == null) {
				String pathPrefix = PathPrefixProperty.get(classInfo);
				String pathSuffix = Objects.requireNonNullElse(PathSuffixProperty.get(classInfo), ".class");
				key = classInfo.getName() + pathSuffix;
				if (pathPrefix != null)
					key = pathPrefix + key;
			} else {
				key =  classInfo.getName();
			}
			map.put(key, classInfo.getBytecode());
			updateProperties(key, classInfo);
		}
	});

This can fix the issue where some files cannot be written, but I am not sure if there are any side effects.

@Col-E
Copy link
Owner

Col-E commented Feb 15, 2025

Please provide a sample so I can step through and see if the suggested fix has any side-effects.

@Col-E
Copy link
Owner

Col-E commented Feb 15, 2025

Additionally, PathOriginalNameProperty is set in BasicResourceImporter.java#L342. The idea is if you have a x.class but its actual name is Foo it will keep the exported name as x.class since that is what was found in the original jar.

There is no inherent cut-off for content with null terminators, so I this may be a slightly different issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants